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