diff options
author | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 03:17:39 +0000 |
---|---|---|
committer | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 03:17:39 +0000 |
commit | f44fe445c4ec85426b5fe3a1d77f13cc96294131 (patch) | |
tree | 29a33bc6a671625599dc89eff2e2c4ddd09cf591 /ui/app_list | |
parent | 3b1282ff6c578334f4e9ac775b245d4d225cd471 (diff) | |
download | chromium_src-f44fe445c4ec85426b5fe3a1d77f13cc96294131.zip chromium_src-f44fe445c4ec85426b5fe3a1d77f13cc96294131.tar.gz chromium_src-f44fe445c4ec85426b5fe3a1d77f13cc96294131.tar.bz2 |
app_list: Add touch drag-n-drop.
- Use a 200ms timer to start touch drag;
- Add 150% scale when touch drag starts;
Also fix a problem that dragged icon goes off when page transition animation
calls Layout() with running BoundsAnimator animation.
BUG=117090
TEST=Verify could use touch to reorder apps in app list.
R=sky@chromium.org,sadrul@chromium.org
Review URL: https://chromiumcodereview.appspot.com/11196011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162630 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/app_list')
-rw-r--r-- | ui/app_list/app_list_item_view.cc | 82 | ||||
-rw-r--r-- | ui/app_list/app_list_item_view.h | 18 | ||||
-rw-r--r-- | ui/app_list/apps_grid_view.cc | 45 | ||||
-rw-r--r-- | ui/app_list/apps_grid_view.h | 17 | ||||
-rw-r--r-- | ui/app_list/apps_grid_view_unittest.cc | 51 |
5 files changed, 176 insertions, 37 deletions
diff --git a/ui/app_list/app_list_item_view.cc b/ui/app_list/app_list_item_view.cc index 6db3fb6..f4f9bd9 100644 --- a/ui/app_list/app_list_item_view.cc +++ b/ui/app_list/app_list_item_view.cc @@ -12,9 +12,12 @@ #include "ui/base/accessibility/accessible_view_state.h" #include "ui/base/animation/throb_animation.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" #include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/transform_util.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/menu/menu_item_view.h" @@ -39,6 +42,12 @@ const SkColor kHighlightedColor = kHoverAndPushedColor; const int kTitleFontSize = 11; const int kLeftRightPaddingChars = 1; +// Delay in milliseconds of when a touch drag should start after tap down. +const int kTouchDragStartDelayInMs = 100; + +// Scale to transform when touch drag starts. +const float kTouchDraggingScale = 1.5f; + const gfx::Font& GetTitleFont() { static gfx::Font* font = NULL; @@ -65,7 +74,8 @@ AppListItemView::AppListItemView(AppsGridView* apps_grid_view, model_(model), apps_grid_view_(apps_grid_view), icon_(new views::ImageView), - title_(new views::Label) { + title_(new views::Label), + touch_dragging_(false) { icon_->set_interactive(false); title_->SetBackgroundColor(0); @@ -121,6 +131,31 @@ void AppListItemView::UpdateIcon() { icon_->SetImage(shadow); } +void AppListItemView::OnTouchDragTimer() { + SetTouchDragging(true); +} + +void AppListItemView::SetTouchDragging(bool touch_dragging) { + if (touch_dragging_ == touch_dragging) + return; + + touch_dragging_ = touch_dragging; + +#if !defined(OS_WIN) + ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); + if (touch_dragging_) { + title_->SetVisible(false); + const gfx::Rect bounds(layer()->bounds().size()); + layer()->SetTransform(gfx::GetScaleTransform( + bounds.CenterPoint(), + kTouchDraggingScale)); + } else { + title_->SetVisible(true); + layer()->SetTransform(gfx::Transform()); + } +#endif +} + void AppListItemView::ItemIconChanged() { UpdateIcon(); } @@ -221,7 +256,7 @@ bool AppListItemView::ShouldEnterPushedState(const ui::Event& event) { bool AppListItemView::OnMousePressed(const ui::MouseEvent& event) { CustomButton::OnMousePressed(event); - apps_grid_view_->InitiateDrag(this, event); + apps_grid_view_->InitiateDrag(this, AppsGridView::MOUSE, event); return true; } @@ -237,8 +272,49 @@ void AppListItemView::OnMouseCaptureLost() { bool AppListItemView::OnMouseDragged(const ui::MouseEvent& event) { CustomButton::OnMouseDragged(event); - apps_grid_view_->UpdateDrag(this, event); + apps_grid_view_->UpdateDrag(this, AppsGridView::MOUSE, event); return true; } +ui::EventResult AppListItemView::OnGestureEvent( + const ui::GestureEvent& event) { + switch (event.type()) { + case ui::ET_GESTURE_TAP_DOWN: + touch_drag_timer_.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds(kTouchDragStartDelayInMs), + this, &AppListItemView::OnTouchDragTimer); + break; + case ui::ET_GESTURE_SCROLL_BEGIN: + if (touch_dragging_) { + apps_grid_view_->InitiateDrag(this, AppsGridView::TOUCH, event); + return ui::ER_CONSUMED; + } else { + touch_drag_timer_.Stop(); + } + break; + case ui::ET_GESTURE_SCROLL_UPDATE: + if (touch_dragging_) { + apps_grid_view_->UpdateDrag(this, AppsGridView::TOUCH, event); + return ui::ER_CONSUMED; + } + break; + case ui::ET_GESTURE_SCROLL_END: + case ui::ET_SCROLL_FLING_START: + if (touch_dragging_) { + SetTouchDragging(false); + apps_grid_view_->EndDrag(false); + return ui::ER_CONSUMED; + } + break; + case ui::ET_GESTURE_END: + case ui::ET_GESTURE_LONG_PRESS: + touch_drag_timer_.Stop(); + SetTouchDragging(false); + break; + default: + break; + } + return CustomButton::OnGestureEvent(event); +} + } // namespace app_list diff --git a/ui/app_list/app_list_item_view.h b/ui/app_list/app_list_item_view.h index 2f057b64..92e90b3 100644 --- a/ui/app_list/app_list_item_view.h +++ b/ui/app_list/app_list_item_view.h @@ -9,6 +9,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/timer.h" #include "ui/app_list/app_list_export.h" #include "ui/app_list/app_list_item_model_observer.h" #include "ui/gfx/shadow_value.h" @@ -46,6 +47,13 @@ class APP_LIST_EXPORT AppListItemView : public views::CustomButton, // Get icon from model and schedule background processing. void UpdateIcon(); + // Invoked when |touch_drag_timer_| fires. It sets touch dragging flag so + // that further touch scroll gestures contribute to drag. + void OnTouchDragTimer(); + + // Sets |touch_dragging_| flag and updates UI. + void SetTouchDragging(bool touch_dragging); + // AppListItemModelObserver overrides: virtual void ItemIconChanged() OVERRIDE; virtual void ItemTitleChanged() OVERRIDE; @@ -70,6 +78,8 @@ class APP_LIST_EXPORT AppListItemView : public views::CustomButton, virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseCaptureLost() OVERRIDE; virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; + virtual ui::EventResult OnGestureEvent( + const ui::GestureEvent& event) OVERRIDE; AppListItemModel* model_; // Owned by AppListModel::Apps. @@ -82,6 +92,14 @@ class APP_LIST_EXPORT AppListItemView : public views::CustomButton, gfx::Size icon_size_; gfx::ShadowValues icon_shadows_; + // A timer to track whether user has pressed on the item long enough. When it + // fires, subsequent scroll gesture events will contribute to drag instead + // scrolling. + base::OneShotTimer<AppListItemView> touch_drag_timer_; + + // True if scroll gestures should contribute to dragging. + bool touch_dragging_; + DISALLOW_COPY_AND_ASSIGN(AppListItemView); }; diff --git a/ui/app_list/apps_grid_view.cc b/ui/app_list/apps_grid_view.cc index 34dac77..96fc4e5 100644 --- a/ui/app_list/apps_grid_view.cc +++ b/ui/app_list/apps_grid_view.cc @@ -49,7 +49,7 @@ AppsGridView::AppsGridView(AppsGridViewDelegate* delegate, rows_per_page_(0), selected_view_(NULL), drag_view_(NULL), - dragging_(false), + drag_pointer_(NONE), page_flip_target_(-1), page_flip_delay_in_ms_(kPageFlipDelayInMs), ALLOW_THIS_IN_INITIALIZER_LIST(bounds_animator_(this)) { @@ -112,6 +112,7 @@ void AppsGridView::EnsureViewVisible(const views::View* view) { } void AppsGridView::InitiateDrag(views::View* view, + Pointer pointer, const ui::LocatedEvent& event) { if (drag_view_) return; @@ -121,16 +122,17 @@ void AppsGridView::InitiateDrag(views::View* view, } void AppsGridView::UpdateDrag(views::View* view, + Pointer pointer, const ui::LocatedEvent& event) { - if (!dragging_ && drag_view_ && + if (!dragging() && drag_view_ && ExceededDragThreshold(event.x() - drag_offset_.x(), event.y() - drag_offset_.y())) { - dragging_ = true; + drag_pointer_ = pointer; // Move the view to the front so that it appears on top of other views. ReorderChildView(drag_view_, -1); bounds_animator_.StopAnimatingView(drag_view_); } - if (dragging_) { + if (drag_pointer_ == pointer) { last_drag_point_ = event.location(); views::View::ConvertPointToTarget(drag_view_, this, &last_drag_point_); @@ -143,16 +145,15 @@ void AppsGridView::UpdateDrag(views::View* view, page_switcher_view_->UpdateUIForDragPoint(page_switcher_point); AnimateToIdealBounds(); - bounds_animator_.StopAnimatingView(drag_view_); drag_view_->SetPosition(last_drag_point_.Subtract(drag_offset_)); } } void AppsGridView::EndDrag(bool cancel) { - if (!cancel && dragging_ && drag_view_ && IsValidIndex(drop_target_)) + if (!cancel && dragging() && drag_view_ && IsValidIndex(drop_target_)) MoveItemInModel(drag_view_, drop_target_); - dragging_ = false; + drag_pointer_ = NONE; drop_target_ = Index(); if (drag_view_) { drag_view_ = NULL; @@ -180,15 +181,14 @@ gfx::Size AppsGridView::GetPreferredSize() { } void AppsGridView::Layout() { - if (bounds_animator_.IsAnimating()) { - AnimateToIdealBounds(); - } else { - CalculateIdealBounds(); - for (int i = 0; i < view_model_.view_size(); ++i) { - views::View* view = view_model_.view_at(i); - if (view != drag_view_) - view->SetBoundsRect(view_model_.ideal_bounds(i)); - } + if (bounds_animator_.IsAnimating()) + bounds_animator_.Cancel(); + + CalculateIdealBounds(); + for (int i = 0; i < view_model_.view_size(); ++i) { + views::View* view = view_model_.view_at(i); + if (view != drag_view_) + view->SetBoundsRect(view_model_.ideal_bounds(i)); } const int page_switcher_height = @@ -412,8 +412,11 @@ void AppsGridView::CalculateIdealBounds() { void AppsGridView::AnimateToIdealBounds() { CalculateIdealBounds(); for (int i = 0; i < view_model_.view_size(); ++i) { - bounds_animator_.AnimateViewTo(view_model_.view_at(i), - view_model_.ideal_bounds(i)); + views::View* view = view_model_.view_at(i); + if (view != drag_view_) { + bounds_animator_.AnimateViewTo(view, + view_model_.ideal_bounds(i)); + } } } @@ -511,7 +514,7 @@ void AppsGridView::MoveItemInModel(views::View* item_view, void AppsGridView::ButtonPressed(views::Button* sender, const ui::Event& event) { - if (dragging_) + if (dragging()) return; if (sender->GetClassName() != AppListItemView::kViewClassName) @@ -525,7 +528,6 @@ void AppsGridView::ButtonPressed(views::Button* sender, void AppsGridView::ListItemsAdded(size_t start, size_t count) { EndDrag(true); - bounds_animator_.Cancel(); for (size_t i = start; i < start + count; ++i) { views::View* view = CreateViewForItemAtIndex(i); @@ -541,7 +543,6 @@ void AppsGridView::ListItemsAdded(size_t start, size_t count) { void AppsGridView::ListItemsRemoved(size_t start, size_t count) { EndDrag(true); - bounds_animator_.Cancel(); for (size_t i = 0; i < count; ++i) { views::View* view = view_model_.view_at(start); @@ -571,7 +572,7 @@ void AppsGridView::TotalPagesChanged() { } void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) { - if (dragging_ && drag_view_) { + if (dragging()) { CalculateDropTarget(last_drag_point_); Layout(); MaybeStartPageFlipTimer(last_drag_point_); diff --git a/ui/app_list/apps_grid_view.h b/ui/app_list/apps_grid_view.h index ccbad1b..31f1cdd 100644 --- a/ui/app_list/apps_grid_view.h +++ b/ui/app_list/apps_grid_view.h @@ -40,6 +40,12 @@ class APP_LIST_EXPORT AppsGridView : public views::View, public ui::ListModelObserver, public PaginationModelObserver { public: + enum Pointer { + NONE, + MOUSE, + TOUCH, + }; + AppsGridView(AppsGridViewDelegate* delegate, PaginationModel* pagination_model); virtual ~AppsGridView(); @@ -59,8 +65,12 @@ class APP_LIST_EXPORT AppsGridView : public views::View, // transition, this does not thing. void EnsureViewVisible(const views::View* view); - void InitiateDrag(views::View* view, const ui::LocatedEvent& event); - void UpdateDrag(views::View* view, const ui::LocatedEvent& event); + void InitiateDrag(views::View* view, + Pointer pointer, + const ui::LocatedEvent& event); + void UpdateDrag(views::View* view, + Pointer pointer, + const ui::LocatedEvent& event); void EndDrag(bool cancel); bool IsDraggedView(const views::View* view) const; @@ -90,6 +100,7 @@ class APP_LIST_EXPORT AppsGridView : public views::View, }; int tiles_per_page() const { return cols_ * rows_per_page_; } + bool dragging() const { return drag_pointer_ != NONE; } // Updates from model. void Update(); @@ -155,7 +166,7 @@ class APP_LIST_EXPORT AppsGridView : public views::View, views::View* drag_view_; gfx::Point drag_offset_; - bool dragging_; + Pointer drag_pointer_; Index drop_target_; gfx::Point last_drag_point_; diff --git a/ui/app_list/apps_grid_view_unittest.cc b/ui/app_list/apps_grid_view_unittest.cc index 4d234b2..ccc7f5c 100644 --- a/ui/app_list/apps_grid_view_unittest.cc +++ b/ui/app_list/apps_grid_view_unittest.cc @@ -164,7 +164,9 @@ class AppsGridViewTest : public testing::Test { } // Points are in |apps_grid_view_|'s coordinates. - void SimulateDrag(const gfx::Point& from, const gfx::Point& to) { + void SimulateDrag(AppsGridView::Pointer pointer, + const gfx::Point& from, + const gfx::Point& to) { AppListItemView* view = GetItemViewForPoint(from); DCHECK(view); @@ -173,11 +175,11 @@ class AppsGridViewTest : public testing::Test { ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated_from, translated_from, 0); - apps_grid_view_->InitiateDrag(view, pressed_event); + apps_grid_view_->InitiateDrag(view, pointer, pressed_event); ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated_to, translated_to, 0); - apps_grid_view_->UpdateDrag(view, drag_event); + apps_grid_view_->UpdateDrag(view, pointer, drag_event); } scoped_ptr<AppListModel::Apps> apps_model_; @@ -252,21 +254,21 @@ TEST_F(AppsGridViewTest, MouseDrag) { gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint(); // Dragging changes model order. - SimulateDrag(from, to); + SimulateDrag(AppsGridView::MOUSE, from, to); apps_grid_view_->EndDrag(false); EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), GetModelContent()); test_api_->LayoutToIdealBounds(); // Canceling drag should keep existing order. - SimulateDrag(from, to); + SimulateDrag(AppsGridView::MOUSE, from, to); apps_grid_view_->EndDrag(true); EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), GetModelContent()); test_api_->LayoutToIdealBounds(); // Deleting an item keeps remaining intact. - SimulateDrag(from, to); + SimulateDrag(AppsGridView::MOUSE, from, to); apps_model_->DeleteAt(1); apps_grid_view_->EndDrag(false); EXPECT_EQ(std::string("Item 1,Item 2,Item 3"), @@ -274,7 +276,7 @@ TEST_F(AppsGridViewTest, MouseDrag) { test_api_->LayoutToIdealBounds(); // Adding a launcher item cancels the drag and respects the order. - SimulateDrag(from, to); + SimulateDrag(AppsGridView::MOUSE, from, to); apps_model_->Add(CreateItem(std::string("Extra"))); apps_grid_view_->EndDrag(false); EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"), @@ -299,7 +301,7 @@ TEST_F(AppsGridViewTest, MouseDragFlipPage) { apps_grid_view_->height() / 2); // Drag to right edge. - SimulateDrag(from, to); + SimulateDrag(AppsGridView::MOUSE, from, to); // Page should be flipped after sometime. EXPECT_TRUE(page_flip_waiter.Wait(100)); @@ -318,7 +320,7 @@ TEST_F(AppsGridViewTest, MouseDragFlipPage) { // Now drag to the left edge and test the other direction. to.set_x(0); - SimulateDrag(from, to); + SimulateDrag(AppsGridView::MOUSE, from, to); EXPECT_TRUE(page_flip_waiter.Wait(100)); EXPECT_EQ(1, pagination_model_->selected_page()); @@ -331,5 +333,36 @@ TEST_F(AppsGridViewTest, MouseDragFlipPage) { apps_grid_view_->EndDrag(true); } +TEST_F(AppsGridViewTest, SimultaneousDrag) { + const int kTotalItems = 4; + PopulateApps(kTotalItems); + EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), + GetModelContent()); + + gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint(); + gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint(); + + gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint(); + gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint(); + + // Starts a mouse drag first then a touch drag. + SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); + SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); + // Finishes the drag and mouse drag wins. + apps_grid_view_->EndDrag(false); + EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), + GetModelContent()); + test_api_->LayoutToIdealBounds(); + + // Starts a touch drag first then a mouse drag. + SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); + SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); + // Finishes the drag and touch drag wins. + apps_grid_view_->EndDrag(false); + EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"), + GetModelContent()); + test_api_->LayoutToIdealBounds(); +} + } // namespace test } // namespace app_list |