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/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);
76 void AppListFolderView::SetAppListFolderItem(AppListFolderItem* folder) {
77 accessible_name_ = ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
78 IDS_APP_LIST_FOLDER_OPEN_FOLDER_ACCESSIBILE_NAME);
79 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
81 folder_item_ = folder;
82 items_grid_view_->SetItemList(folder_item_->item_list());
83 folder_header_view_->SetFolderItem(folder_item_);
86 void AppListFolderView::ScheduleShowHideAnimation(bool show,
87 bool hide_for_reparent) {
88 hide_for_reparent_ = hide_for_reparent;
90 // Stop any previous animation.
91 layer()->GetAnimator()->StopAnimating();
93 // Hide the top items temporarily if showing the view for opening the folder.
95 items_grid_view_->SetTopItemViewsVisible(false);
98 layer()->SetOpacity(show ? 0.0f : 1.0f);
100 UpdateFolderNameVisibility(true);
102 ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
103 animation.SetTweenType(
104 show ? kFolderFadeInTweenType : kFolderFadeOutTweenType);
105 animation.AddObserver(this);
106 animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
107 show ? kFolderTransitionInDurationMs : kFolderTransitionOutDurationMs));
109 layer()->SetOpacity(show ? 1.0f : 0.0f);
112 gfx::Size AppListFolderView::GetPreferredSize() const {
113 const gfx::Size header_size = folder_header_view_->GetPreferredSize();
114 const gfx::Size grid_size = items_grid_view_->GetPreferredSize();
115 int width = std::max(header_size.width(), grid_size.width());
116 int height = header_size.height() + grid_size.height();
117 return gfx::Size(width, height);
120 void AppListFolderView::Layout() {
121 CalculateIdealBounds();
122 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
125 bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) {
126 return items_grid_view_->OnKeyPressed(event);
129 void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem* item) {
130 if (item == folder_item_) {
131 items_grid_view_->OnFolderItemRemoved();
132 folder_header_view_->OnFolderItemRemoved();
135 // Do not change state if it is hidden.
136 if (hide_for_reparent_ || layer()->opacity() == 0.0f)
139 // If the folder item associated with this view is removed from the model,
140 // (e.g. the last item in the folder was deleted), reset the view and signal
141 // the container view to show the app list instead.
142 // Pass NULL to ShowApps() to avoid triggering animation from the deleted
144 container_view_->ShowApps(NULL);
148 void AppListFolderView::OnImplicitAnimationsCompleted() {
149 // Show the top items when the opening folder animation is done.
150 if (layer()->opacity() == 1.0f)
151 items_grid_view_->SetTopItemViewsVisible(true);
153 // If the view is hidden for reparenting a folder item, it has to be visible,
154 // so that drag_view_ can keep receiving mouse events.
155 if (layer()->opacity() == 0.0f && !hide_for_reparent_)
158 // Set the view bounds to a small rect, so that it won't overlap the root
159 // level apps grid view during folder item reprenting transitional period.
160 if (hide_for_reparent_)
161 SetBoundsRect(gfx::Rect(bounds().x(), bounds().y(), 1, 1));
164 void AppListFolderView::CalculateIdealBounds() {
165 gfx::Rect rect(GetContentsBounds());
169 gfx::Rect header_frame(rect);
170 gfx::Size size = folder_header_view_->GetPreferredSize();
171 header_frame.set_height(size.height());
172 view_model_->set_ideal_bounds(kIndexFolderHeader, header_frame);
174 gfx::Rect grid_frame(rect);
175 grid_frame.Subtract(header_frame);
176 view_model_->set_ideal_bounds(kIndexChildItems, grid_frame);
179 void AppListFolderView::StartSetupDragInRootLevelAppsGridView(
180 AppListItemView* original_drag_view,
181 const gfx::Point& drag_point_in_root_grid,
182 bool has_native_drag) {
183 // Converts the original_drag_view's bounds to the coordinate system of
184 // root level grid view.
185 gfx::RectF rect_f(original_drag_view->bounds());
186 views::View::ConvertRectToTarget(items_grid_view_,
187 container_view_->apps_grid_view(),
189 gfx::Rect rect_in_root_grid_view = gfx::ToEnclosingRect(rect_f);
191 container_view_->apps_grid_view()
192 ->InitiateDragFromReparentItemInRootLevelGridView(original_drag_view,
193 rect_in_root_grid_view,
194 drag_point_in_root_grid,
198 gfx::Rect AppListFolderView::GetItemIconBoundsAt(int index) {
199 AppListItemView* item_view = items_grid_view_->GetItemViewAt(index);
200 // Icon bounds relative to AppListItemView.
201 const gfx::Rect icon_bounds = item_view->GetIconBounds();
202 gfx::Rect to_apps_grid_view = item_view->ConvertRectToParent(icon_bounds);
203 gfx::Rect to_folder =
204 items_grid_view_->ConvertRectToParent(to_apps_grid_view);
206 // Get the icon image's bound.
207 to_folder.ClampToCenteredSize(
208 gfx::Size(kGridIconDimension, kGridIconDimension));
213 void AppListFolderView::UpdateFolderViewBackground(bool show_bubble) {
214 if (hide_for_reparent_)
217 // Before showing the folder container inking bubble, hide the folder name.
219 UpdateFolderNameVisibility(false);
221 container_view_->folder_background_view()->UpdateFolderContainerBubble(
222 show_bubble ? FolderBackgroundView::SHOW_BUBBLE :
223 FolderBackgroundView::HIDE_BUBBLE);
226 void AppListFolderView::UpdateFolderNameVisibility(bool visible) {
227 folder_header_view_->UpdateFolderNameVisibility(visible);
230 bool AppListFolderView::IsPointOutsideOfFolderBoundary(
231 const gfx::Point& point) {
232 if (!GetLocalBounds().Contains(point))
235 gfx::Point center = GetLocalBounds().CenterPoint();
236 float delta = (point - center).Length();
237 return delta > container_view_->folder_background_view()->
238 GetFolderContainerBubbleRadius() + kOutOfFolderContainerBubbleDelta;
241 // When user drags a folder item out of the folder boundary ink bubble, the
242 // folder view UI will be hidden, and switch back to top level AppsGridView.
243 // The dragged item will seamlessly move on the top level AppsGridView.
244 // In order to achieve the above, we keep the folder view and its child grid
245 // view visible with opacity 0, so that the drag_view_ on the hidden grid view
246 // will keep receiving mouse event. At the same time, we initiated a new
247 // drag_view_ in the top level grid view, and keep it moving with the hidden
248 // grid view's drag_view_, so that the dragged item can be engaged in drag and
249 // drop flow in the top level grid view. During the reparenting process, the
250 // drag_view_ in hidden grid view will dispatch the drag and drop event to
251 // the top level grid view, until the drag ends.
252 void AppListFolderView::ReparentItem(
253 AppListItemView* original_drag_view,
254 const gfx::Point& drag_point_in_folder_grid,
255 bool has_native_drag) {
256 // Convert the drag point relative to the root level AppsGridView.
257 gfx::Point to_root_level_grid = drag_point_in_folder_grid;
258 ConvertPointToTarget(items_grid_view_,
259 container_view_->apps_grid_view(),
260 &to_root_level_grid);
261 StartSetupDragInRootLevelAppsGridView(
262 original_drag_view, to_root_level_grid, has_native_drag);
263 container_view_->ReparentFolderItemTransit(folder_item_);
266 void AppListFolderView::DispatchDragEventForReparent(
267 AppsGridView::Pointer pointer,
268 const gfx::Point& drag_point_in_folder_grid) {
269 AppsGridView* root_grid = container_view_->apps_grid_view();
270 gfx::Point drag_point_in_root_grid = drag_point_in_folder_grid;
271 ConvertPointToTarget(items_grid_view_, root_grid, &drag_point_in_root_grid);
272 root_grid->UpdateDragFromReparentItem(pointer, drag_point_in_folder_grid);
275 void AppListFolderView::DispatchEndDragEventForReparent(
276 bool events_forwarded_to_drag_drop_host,
278 container_view_->apps_grid_view()->EndDragFromReparentItemInRootLevel(
279 events_forwarded_to_drag_drop_host, cancel_drag);
282 void AppListFolderView::HideViewImmediately() {
284 hide_for_reparent_ = false;
287 void AppListFolderView::CloseFolderPage() {
288 accessible_name_ = ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
289 IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME);
290 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
292 GiveBackFocusToSearchBox();
293 if (items_grid_view()->dragging())
294 items_grid_view()->EndDrag(true);
295 items_grid_view()->ClearAnySelectedView();
296 container_view_->ShowApps(folder_item_);
299 bool AppListFolderView::IsOEMFolder() const {
300 return folder_item_->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM;
303 void AppListFolderView::SetRootLevelDragViewVisible(bool visible) {
304 container_view_->apps_grid_view()->SetDragViewVisible(visible);
307 void AppListFolderView::GetAccessibleState(ui::AXViewState* state) {
308 state->role = ui::AX_ROLE_BUTTON;
309 state->name = accessible_name_;
312 void AppListFolderView::NavigateBack(AppListFolderItem* item,
313 const ui::Event& event_flags) {
317 void AppListFolderView::GiveBackFocusToSearchBox() {
318 app_list_main_view_->search_box_view()->search_box()->RequestFocus();
321 void AppListFolderView::SetItemName(AppListFolderItem* item,
322 const std::string& name) {
323 model_->SetItemName(item, name);
326 } // namespace app_list