app_list_folder_view.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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.
4
5#include "ui/app_list/views/app_list_folder_view.h"
6
7#include <algorithm>
8
9#include "grit/ui_strings.h"
10#include "ui/accessibility/ax_view_state.h"
11#include "ui/app_list/app_list_constants.h"
12#include "ui/app_list/app_list_folder_item.h"
13#include "ui/app_list/app_list_model.h"
14#include "ui/app_list/pagination_model.h"
15#include "ui/app_list/views/app_list_item_view.h"
16#include "ui/app_list/views/app_list_main_view.h"
17#include "ui/app_list/views/apps_container_view.h"
18#include "ui/app_list/views/apps_grid_view.h"
19#include "ui/app_list/views/contents_view.h"
20#include "ui/app_list/views/folder_background_view.h"
21#include "ui/app_list/views/folder_header_view.h"
22#include "ui/app_list/views/search_box_view.h"
23#include "ui/compositor/scoped_layer_animation_settings.h"
24#include "ui/events/event.h"
25#include "ui/gfx/rect_conversions.h"
26#include "ui/views/controls/textfield/textfield.h"
27#include "ui/views/view_model.h"
28#include "ui/views/view_model_utils.h"
29
30namespace app_list {
31
32namespace {
33
34// Indexes of interesting views in ViewModel of AppListFolderView.
35const int kIndexFolderHeader = 0;
36const int kIndexChildItems = 1;
37
38// Threshold for the distance from the center of the item to the circle of the
39// folder container ink bubble, beyond which, the item is considered dragged
40// out of the folder boundary.
41const int kOutOfFolderContainerBubbleDelta = 30;
42
43}  // namespace
44
45AppListFolderView::AppListFolderView(AppsContainerView* container_view,
46                                     AppListModel* model,
47                                     AppListMainView* app_list_main_view)
48    : container_view_(container_view),
49      app_list_main_view_(app_list_main_view),
50      folder_header_view_(new FolderHeaderView(this)),
51      view_model_(new views::ViewModel),
52      model_(model),
53      folder_item_(NULL),
54      pagination_model_(new PaginationModel),
55      hide_for_reparent_(false) {
56  AddChildView(folder_header_view_);
57  view_model_->Add(folder_header_view_, kIndexFolderHeader);
58
59  items_grid_view_ =
60      new AppsGridView(app_list_main_view_, pagination_model_.get());
61  items_grid_view_->set_folder_delegate(this);
62  items_grid_view_->SetLayout(
63      kPreferredIconDimension,
64      container_view->apps_grid_view()->cols(),
65      container_view->apps_grid_view()->rows_per_page());
66  items_grid_view_->SetModel(model);
67  AddChildView(items_grid_view_);
68  view_model_->Add(items_grid_view_, kIndexChildItems);
69
70#if defined(USE_AURA)
71  SetPaintToLayer(true);
72  SetFillsBoundsOpaquely(false);
73#endif
74
75  model_->AddObserver(this);
76}
77
78AppListFolderView::~AppListFolderView() {
79  model_->RemoveObserver(this);
80  // Make sure |items_grid_view_| is deleted before |pagination_model_|.
81  RemoveAllChildViews(true);
82}
83
84void AppListFolderView::SetAppListFolderItem(AppListFolderItem* folder) {
85  accessible_name_ = ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
86      IDS_APP_LIST_FOLDER_OPEN_FOLDER_ACCESSIBILE_NAME);
87  NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
88
89  folder_item_ = folder;
90  items_grid_view_->SetItemList(folder_item_->item_list());
91  folder_header_view_->SetFolderItem(folder_item_);
92}
93
94void AppListFolderView::ScheduleShowHideAnimation(bool show,
95                                                  bool hide_for_reparent) {
96  hide_for_reparent_ = hide_for_reparent;
97
98  // Stop any previous animation.
99  layer()->GetAnimator()->StopAnimating();
100
101  // Hide the top items temporarily if showing the view for opening the folder.
102  if (show)
103    items_grid_view_->SetTopItemViewsVisible(false);
104
105  // Set initial state.
106  layer()->SetOpacity(show ? 0.0f : 1.0f);
107  SetVisible(true);
108  UpdateFolderNameVisibility(true);
109
110  ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
111  animation.SetTweenType(
112      show ? kFolderFadeInTweenType : kFolderFadeOutTweenType);
113  animation.AddObserver(this);
114  animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
115      show ? kFolderTransitionInDurationMs : kFolderTransitionOutDurationMs));
116
117  layer()->SetOpacity(show ? 1.0f : 0.0f);
118}
119
120gfx::Size AppListFolderView::GetPreferredSize() const {
121  const gfx::Size header_size = folder_header_view_->GetPreferredSize();
122  const gfx::Size grid_size = items_grid_view_->GetPreferredSize();
123  int width = std::max(header_size.width(), grid_size.width());
124  int height = header_size.height() + grid_size.height();
125  return gfx::Size(width, height);
126}
127
128void AppListFolderView::Layout() {
129  CalculateIdealBounds();
130  views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
131}
132
133bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) {
134  return items_grid_view_->OnKeyPressed(event);
135}
136
137void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem* item) {
138  if (item == folder_item_) {
139    items_grid_view_->OnFolderItemRemoved();
140    folder_header_view_->OnFolderItemRemoved();
141    folder_item_ = NULL;
142
143    // Do not change state if it is hidden.
144    if (hide_for_reparent_ || layer()->opacity() == 0.0f)
145      return;
146
147    // If the folder item associated with this view is removed from the model,
148    // (e.g. the last item in the folder was deleted), reset the view and signal
149    // the container view to show the app list instead.
150    // Pass NULL to ShowApps() to avoid triggering animation from the deleted
151    // folder.
152    container_view_->ShowApps(NULL);
153  }
154}
155
156void AppListFolderView::OnImplicitAnimationsCompleted() {
157  // Show the top items when the opening folder animation is done.
158  if (layer()->opacity() == 1.0f)
159    items_grid_view_->SetTopItemViewsVisible(true);
160
161  // If the view is hidden for reparenting a folder item, it has to be visible,
162  // so that drag_view_ can keep receiving mouse events.
163  if (layer()->opacity() == 0.0f && !hide_for_reparent_)
164    SetVisible(false);
165
166  // Set the view bounds to a small rect, so that it won't overlap the root
167  // level apps grid view during folder item reprenting transitional period.
168  if (hide_for_reparent_)
169    SetBoundsRect(gfx::Rect(bounds().x(), bounds().y(), 1, 1));
170}
171
172void AppListFolderView::CalculateIdealBounds() {
173  gfx::Rect rect(GetContentsBounds());
174  if (rect.IsEmpty())
175    return;
176
177  gfx::Rect header_frame(rect);
178  gfx::Size size = folder_header_view_->GetPreferredSize();
179  header_frame.set_height(size.height());
180  view_model_->set_ideal_bounds(kIndexFolderHeader, header_frame);
181
182  gfx::Rect grid_frame(rect);
183  grid_frame.Subtract(header_frame);
184  view_model_->set_ideal_bounds(kIndexChildItems, grid_frame);
185}
186
187void AppListFolderView::StartSetupDragInRootLevelAppsGridView(
188    AppListItemView* original_drag_view,
189    const gfx::Point& drag_point_in_root_grid) {
190  // Converts the original_drag_view's bounds to the coordinate system of
191  // root level grid view.
192  gfx::RectF rect_f(original_drag_view->bounds());
193  views::View::ConvertRectToTarget(items_grid_view_,
194                                   container_view_->apps_grid_view(),
195                                   &rect_f);
196  gfx::Rect rect_in_root_grid_view = gfx::ToEnclosingRect(rect_f);
197
198  container_view_->apps_grid_view()->
199      InitiateDragFromReparentItemInRootLevelGridView(
200          original_drag_view, rect_in_root_grid_view, drag_point_in_root_grid);
201}
202
203gfx::Rect AppListFolderView::GetItemIconBoundsAt(int index) {
204  AppListItemView* item_view = items_grid_view_->GetItemViewAt(index);
205  // Icon bounds relative to AppListItemView.
206  const gfx::Rect icon_bounds = item_view->GetIconBounds();
207  gfx::Rect to_apps_grid_view = item_view->ConvertRectToParent(icon_bounds);
208  gfx::Rect to_folder =
209      items_grid_view_->ConvertRectToParent(to_apps_grid_view);
210
211  // Get the icon image's bound.
212  to_folder.ClampToCenteredSize(
213      gfx::Size(kPreferredIconDimension, kPreferredIconDimension));
214
215  return to_folder;
216}
217
218void AppListFolderView::UpdateFolderViewBackground(bool show_bubble) {
219  if (hide_for_reparent_)
220    return;
221
222  // Before showing the folder container inking bubble, hide the folder name.
223  if (show_bubble)
224    UpdateFolderNameVisibility(false);
225
226  container_view_->folder_background_view()->UpdateFolderContainerBubble(
227      show_bubble ? FolderBackgroundView::SHOW_BUBBLE :
228                    FolderBackgroundView::HIDE_BUBBLE);
229}
230
231void AppListFolderView::UpdateFolderNameVisibility(bool visible) {
232  folder_header_view_->UpdateFolderNameVisibility(visible);
233}
234
235bool AppListFolderView::IsPointOutsideOfFolderBoundary(
236    const gfx::Point& point) {
237  if (!GetLocalBounds().Contains(point))
238    return true;
239
240  gfx::Point center = GetLocalBounds().CenterPoint();
241  float delta = (point - center).Length();
242  return delta > container_view_->folder_background_view()->
243      GetFolderContainerBubbleRadius() + kOutOfFolderContainerBubbleDelta;
244}
245
246// When user drags a folder item out of the folder boundary ink bubble, the
247// folder view UI will be hidden, and switch back to top level AppsGridView.
248// The dragged item will seamlessly move on the top level AppsGridView.
249// In order to achieve the above, we keep the folder view and its child grid
250// view visible with opacity 0, so that the drag_view_ on the hidden grid view
251// will keep receiving mouse event. At the same time, we initiated a new
252// drag_view_ in the top level grid view, and keep it moving with the hidden
253// grid view's drag_view_, so that the dragged item can be engaged in drag and
254// drop flow in the top level grid view. During the reparenting process, the
255// drag_view_ in hidden grid view will dispatch the drag and drop event to
256// the top level grid view, until the drag ends.
257void AppListFolderView::ReparentItem(
258    AppListItemView* original_drag_view,
259    const gfx::Point& drag_point_in_folder_grid) {
260  // Convert the drag point relative to the root level AppsGridView.
261  gfx::Point to_root_level_grid = drag_point_in_folder_grid;
262  ConvertPointToTarget(items_grid_view_,
263                       container_view_->apps_grid_view(),
264                       &to_root_level_grid);
265  StartSetupDragInRootLevelAppsGridView(original_drag_view, to_root_level_grid);
266  container_view_->ReparentFolderItemTransit(folder_item_);
267}
268
269void AppListFolderView::DispatchDragEventForReparent(
270    AppsGridView::Pointer pointer,
271    const gfx::Point& drag_point_in_folder_grid) {
272  AppsGridView* root_grid = container_view_->apps_grid_view();
273  gfx::Point drag_point_in_root_grid = drag_point_in_folder_grid;
274  ConvertPointToTarget(items_grid_view_, root_grid, &drag_point_in_root_grid);
275  root_grid->UpdateDragFromReparentItem(pointer, drag_point_in_folder_grid);
276}
277
278void AppListFolderView::DispatchEndDragEventForReparent(
279    bool events_forwarded_to_drag_drop_host,
280    bool cancel_drag) {
281  container_view_->apps_grid_view()->EndDragFromReparentItemInRootLevel(
282      events_forwarded_to_drag_drop_host, cancel_drag);
283}
284
285void AppListFolderView::HideViewImmediately() {
286  SetVisible(false);
287  hide_for_reparent_ = false;
288}
289
290void AppListFolderView::CloseFolderPage() {
291  accessible_name_ = ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
292      IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME);
293  NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
294
295  GiveBackFocusToSearchBox();
296  if (items_grid_view()->dragging())
297    items_grid_view()->EndDrag(true);
298  items_grid_view()->ClearAnySelectedView();
299  container_view_->ShowApps(folder_item_);
300}
301
302bool AppListFolderView::IsOEMFolder() const {
303  return folder_item_->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM;
304}
305
306void AppListFolderView::SetRootLevelDragViewVisible(bool visible) {
307  container_view_->apps_grid_view()->SetDragViewVisible(visible);
308}
309
310void AppListFolderView::GetAccessibleState(ui::AXViewState* state) {
311  state->role = ui::AX_ROLE_BUTTON;
312  state->name = accessible_name_;
313}
314
315void AppListFolderView::NavigateBack(AppListFolderItem* item,
316                                     const ui::Event& event_flags) {
317  CloseFolderPage();
318}
319
320void AppListFolderView::GiveBackFocusToSearchBox() {
321  app_list_main_view_->search_box_view()->search_box()->RequestFocus();
322}
323
324void AppListFolderView::SetItemName(AppListFolderItem* item,
325                                    const std::string& name) {
326  model_->SetItemName(item, name);
327}
328
329}  // namespace app_list
330