Home | History | Annotate | Download | only in views
      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 
     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       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 
     73 AppListFolderView::~AppListFolderView() {
     74   model_->RemoveObserver(this);
     75 }
     76 
     77 void 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 
     87 void 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 
    113 gfx::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 
    121 void AppListFolderView::Layout() {
    122   CalculateIdealBounds();
    123   views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
    124 }
    125 
    126 bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) {
    127   return items_grid_view_->OnKeyPressed(event);
    128 }
    129 
    130 void 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 
    149 void 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 
    165 void 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 
    180 void 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 
    196 gfx::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 
    211 void 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 
    224 void AppListFolderView::UpdateFolderNameVisibility(bool visible) {
    225   folder_header_view_->UpdateFolderNameVisibility(visible);
    226 }
    227 
    228 bool 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.
    250 void 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 
    262 void 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 
    271 void 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 
    278 void AppListFolderView::HideViewImmediately() {
    279   SetVisible(false);
    280   hide_for_reparent_ = false;
    281 }
    282 
    283 void 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 
    295 bool AppListFolderView::IsOEMFolder() const {
    296   return folder_item_->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM;
    297 }
    298 
    299 void AppListFolderView::SetRootLevelDragViewVisible(bool visible) {
    300   container_view_->apps_grid_view()->SetDragViewVisible(visible);
    301 }
    302 
    303 void AppListFolderView::GetAccessibleState(ui::AXViewState* state) {
    304   state->role = ui::AX_ROLE_BUTTON;
    305   state->name = accessible_name_;
    306 }
    307 
    308 void AppListFolderView::NavigateBack(AppListFolderItem* item,
    309                                      const ui::Event& event_flags) {
    310   CloseFolderPage();
    311 }
    312 
    313 void AppListFolderView::GiveBackFocusToSearchBox() {
    314   app_list_main_view_->search_box_view()->search_box()->RequestFocus();
    315 }
    316 
    317 void AppListFolderView::SetItemName(AppListFolderItem* item,
    318                                     const std::string& name) {
    319   model_->SetItemName(item, name);
    320 }
    321 
    322 }  // namespace app_list
    323