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 "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" 28 29 namespace app_list { 30 31 namespace { 32 33 // Indexes of interesting views in ViewModel of AppListFolderView. 34 const int kIndexFolderHeader = 0; 35 const int kIndexChildItems = 1; 36 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; 41 42 } // namespace 43 44 AppListFolderView::AppListFolderView(AppsContainerView* container_view, 45 AppListModel* model, 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), 51 model_(model), 52 folder_item_(NULL), 53 hide_for_reparent_(false) { 54 AddChildView(folder_header_view_); 55 view_model_->Add(folder_header_view_, kIndexFolderHeader); 56 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); 65 66 SetPaintToLayer(true); 67 SetFillsBoundsOpaquely(false); 68 69 model_->AddObserver(this); 70 } 71 72 AppListFolderView::~AppListFolderView() { 73 model_->RemoveObserver(this); 74 } 75 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); 80 81 folder_item_ = folder; 82 items_grid_view_->SetItemList(folder_item_->item_list()); 83 folder_header_view_->SetFolderItem(folder_item_); 84 } 85 86 void AppListFolderView::ScheduleShowHideAnimation(bool show, 87 bool hide_for_reparent) { 88 hide_for_reparent_ = hide_for_reparent; 89 90 // Stop any previous animation. 91 layer()->GetAnimator()->StopAnimating(); 92 93 // Hide the top items temporarily if showing the view for opening the folder. 94 if (show) 95 items_grid_view_->SetTopItemViewsVisible(false); 96 97 // Set initial state. 98 layer()->SetOpacity(show ? 0.0f : 1.0f); 99 SetVisible(true); 100 UpdateFolderNameVisibility(true); 101 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)); 108 109 layer()->SetOpacity(show ? 1.0f : 0.0f); 110 } 111 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); 118 } 119 120 void AppListFolderView::Layout() { 121 CalculateIdealBounds(); 122 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_); 123 } 124 125 bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) { 126 return items_grid_view_->OnKeyPressed(event); 127 } 128 129 void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem* item) { 130 if (item == folder_item_) { 131 items_grid_view_->OnFolderItemRemoved(); 132 folder_header_view_->OnFolderItemRemoved(); 133 folder_item_ = NULL; 134 135 // Do not change state if it is hidden. 136 if (hide_for_reparent_ || layer()->opacity() == 0.0f) 137 return; 138 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 143 // folder. 144 container_view_->ShowApps(NULL); 145 } 146 } 147 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); 152 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_) 156 SetVisible(false); 157 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)); 162 } 163 164 void AppListFolderView::CalculateIdealBounds() { 165 gfx::Rect rect(GetContentsBounds()); 166 if (rect.IsEmpty()) 167 return; 168 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); 173 174 gfx::Rect grid_frame(rect); 175 grid_frame.Subtract(header_frame); 176 view_model_->set_ideal_bounds(kIndexChildItems, grid_frame); 177 } 178 179 void AppListFolderView::StartSetupDragInRootLevelAppsGridView( 180 AppListItemView* original_drag_view, 181 const gfx::Point& drag_point_in_root_grid) { 182 // Converts the original_drag_view's bounds to the coordinate system of 183 // root level grid view. 184 gfx::RectF rect_f(original_drag_view->bounds()); 185 views::View::ConvertRectToTarget(items_grid_view_, 186 container_view_->apps_grid_view(), 187 &rect_f); 188 gfx::Rect rect_in_root_grid_view = gfx::ToEnclosingRect(rect_f); 189 190 container_view_->apps_grid_view()-> 191 InitiateDragFromReparentItemInRootLevelGridView( 192 original_drag_view, rect_in_root_grid_view, drag_point_in_root_grid); 193 } 194 195 gfx::Rect AppListFolderView::GetItemIconBoundsAt(int index) { 196 AppListItemView* item_view = items_grid_view_->GetItemViewAt(index); 197 // Icon bounds relative to AppListItemView. 198 const gfx::Rect icon_bounds = item_view->GetIconBounds(); 199 gfx::Rect to_apps_grid_view = item_view->ConvertRectToParent(icon_bounds); 200 gfx::Rect to_folder = 201 items_grid_view_->ConvertRectToParent(to_apps_grid_view); 202 203 // Get the icon image's bound. 204 to_folder.ClampToCenteredSize( 205 gfx::Size(kGridIconDimension, kGridIconDimension)); 206 207 return to_folder; 208 } 209 210 void AppListFolderView::UpdateFolderViewBackground(bool show_bubble) { 211 if (hide_for_reparent_) 212 return; 213 214 // Before showing the folder container inking bubble, hide the folder name. 215 if (show_bubble) 216 UpdateFolderNameVisibility(false); 217 218 container_view_->folder_background_view()->UpdateFolderContainerBubble( 219 show_bubble ? FolderBackgroundView::SHOW_BUBBLE : 220 FolderBackgroundView::HIDE_BUBBLE); 221 } 222 223 void AppListFolderView::UpdateFolderNameVisibility(bool visible) { 224 folder_header_view_->UpdateFolderNameVisibility(visible); 225 } 226 227 bool AppListFolderView::IsPointOutsideOfFolderBoundary( 228 const gfx::Point& point) { 229 if (!GetLocalBounds().Contains(point)) 230 return true; 231 232 gfx::Point center = GetLocalBounds().CenterPoint(); 233 float delta = (point - center).Length(); 234 return delta > container_view_->folder_background_view()-> 235 GetFolderContainerBubbleRadius() + kOutOfFolderContainerBubbleDelta; 236 } 237 238 // When user drags a folder item out of the folder boundary ink bubble, the 239 // folder view UI will be hidden, and switch back to top level AppsGridView. 240 // The dragged item will seamlessly move on the top level AppsGridView. 241 // In order to achieve the above, we keep the folder view and its child grid 242 // view visible with opacity 0, so that the drag_view_ on the hidden grid view 243 // will keep receiving mouse event. At the same time, we initiated a new 244 // drag_view_ in the top level grid view, and keep it moving with the hidden 245 // grid view's drag_view_, so that the dragged item can be engaged in drag and 246 // drop flow in the top level grid view. During the reparenting process, the 247 // drag_view_ in hidden grid view will dispatch the drag and drop event to 248 // the top level grid view, until the drag ends. 249 void AppListFolderView::ReparentItem( 250 AppListItemView* original_drag_view, 251 const gfx::Point& drag_point_in_folder_grid) { 252 // Convert the drag point relative to the root level AppsGridView. 253 gfx::Point to_root_level_grid = drag_point_in_folder_grid; 254 ConvertPointToTarget(items_grid_view_, 255 container_view_->apps_grid_view(), 256 &to_root_level_grid); 257 StartSetupDragInRootLevelAppsGridView(original_drag_view, to_root_level_grid); 258 container_view_->ReparentFolderItemTransit(folder_item_); 259 } 260 261 void AppListFolderView::DispatchDragEventForReparent( 262 AppsGridView::Pointer pointer, 263 const gfx::Point& drag_point_in_folder_grid) { 264 AppsGridView* root_grid = container_view_->apps_grid_view(); 265 gfx::Point drag_point_in_root_grid = drag_point_in_folder_grid; 266 ConvertPointToTarget(items_grid_view_, root_grid, &drag_point_in_root_grid); 267 root_grid->UpdateDragFromReparentItem(pointer, drag_point_in_folder_grid); 268 } 269 270 void AppListFolderView::DispatchEndDragEventForReparent( 271 bool events_forwarded_to_drag_drop_host, 272 bool cancel_drag) { 273 container_view_->apps_grid_view()->EndDragFromReparentItemInRootLevel( 274 events_forwarded_to_drag_drop_host, cancel_drag); 275 } 276 277 void AppListFolderView::HideViewImmediately() { 278 SetVisible(false); 279 hide_for_reparent_ = false; 280 } 281 282 void AppListFolderView::CloseFolderPage() { 283 accessible_name_ = ui::ResourceBundle::GetSharedInstance().GetLocalizedString( 284 IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME); 285 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); 286 287 GiveBackFocusToSearchBox(); 288 if (items_grid_view()->dragging()) 289 items_grid_view()->EndDrag(true); 290 items_grid_view()->ClearAnySelectedView(); 291 container_view_->ShowApps(folder_item_); 292 } 293 294 bool AppListFolderView::IsOEMFolder() const { 295 return folder_item_->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM; 296 } 297 298 void AppListFolderView::SetRootLevelDragViewVisible(bool visible) { 299 container_view_->apps_grid_view()->SetDragViewVisible(visible); 300 } 301 302 void AppListFolderView::GetAccessibleState(ui::AXViewState* state) { 303 state->role = ui::AX_ROLE_BUTTON; 304 state->name = accessible_name_; 305 } 306 307 void AppListFolderView::NavigateBack(AppListFolderItem* item, 308 const ui::Event& event_flags) { 309 CloseFolderPage(); 310 } 311 312 void AppListFolderView::GiveBackFocusToSearchBox() { 313 app_list_main_view_->search_box_view()->search_box()->RequestFocus(); 314 } 315 316 void AppListFolderView::SetItemName(AppListFolderItem* item, 317 const std::string& name) { 318 model_->SetItemName(item, name); 319 } 320 321 } // namespace app_list 322