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