summaryrefslogtreecommitdiffstats
path: root/ui/app_list
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 03:17:39 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 03:17:39 +0000
commitf44fe445c4ec85426b5fe3a1d77f13cc96294131 (patch)
tree29a33bc6a671625599dc89eff2e2c4ddd09cf591 /ui/app_list
parent3b1282ff6c578334f4e9ac775b245d4d225cd471 (diff)
downloadchromium_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.cc82
-rw-r--r--ui/app_list/app_list_item_view.h18
-rw-r--r--ui/app_list/apps_grid_view.cc45
-rw-r--r--ui/app_list/apps_grid_view.h17
-rw-r--r--ui/app_list/apps_grid_view_unittest.cc51
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