Commit 31438b99 authored by sammiequon's avatar sammiequon Committed by Commit bot

shelf: Allow dragging items from main shelf to overflow shelf.

Previously only overflow shelf to main shelf, now bidirectional.

TEST=ash_unittests --gtest_filter="ShelfViewTest.*"
BUG=710228

Review-Url: https://codereview.chromium.org/2820693004
Cr-Commit-Position: refs/heads/master@{#469368}
parent d9c705db
......@@ -728,8 +728,7 @@ void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
bool visible = i >= first_visible_index_ && i <= last_visible_index_;
// To track the dragging of |drag_view_| continuously, its visibility
// should be always true regardless of its position.
if (dragged_off_from_overflow_to_shelf_ &&
view_model_->view_at(i) == drag_view_)
if (dragged_to_another_shelf_ && view_model_->view_at(i) == drag_view_)
view_model_->view_at(i)->SetVisible(true);
else
view_model_->view_at(i)->SetVisible(visible);
......@@ -1034,6 +1033,15 @@ void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
bounds_animator_->StopAnimatingView(drag_view_);
}
void ShelfView::EndDragOnOtherShelf(bool cancel) {
if (is_overflow_mode()) {
main_shelf_->EndDrag(cancel);
} else {
DCHECK(overflow_bubble_->IsShowing());
overflow_bubble_->shelf_view()->EndDrag(cancel);
}
}
bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
int current_index = view_model_->GetIndexOfView(drag_view_);
DCHECK_NE(-1, current_index);
......@@ -1051,17 +1059,18 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
// If the shelf/overflow bubble bounds contains |screen_location| we insert
// the item back into the shelf.
if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
if (dragged_off_from_overflow_to_shelf_) {
if (dragged_to_another_shelf_) {
// During the dragging an item from Shelf to Overflow, it can enter here
// directly because both are located very closly.
main_shelf_->EndDrag(true);
// directly because both are located very closely.
EndDragOnOtherShelf(true /* cancel */);
// Stops the animation of |drag_view_| and sets its bounds explicitly
// becase ContinueDrag() stops its animation. Without this, unexpected
// because ContinueDrag() stops its animation. Without this, unexpected
// bounds will be set.
bounds_animator_->StopAnimatingView(drag_view_);
int drag_view_index = view_model_->GetIndexOfView(drag_view_);
drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
dragged_off_from_overflow_to_shelf_ = false;
dragged_to_another_shelf_ = false;
}
// Destroy our proxy view item.
DestroyDragIconProxy();
......@@ -1077,18 +1086,42 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
} else if (is_overflow_mode() &&
main_shelf_->GetBoundsForDragInsertInScreen().Contains(
screen_location)) {
if (!dragged_off_from_overflow_to_shelf_) {
dragged_off_from_overflow_to_shelf_ = true;
// The item was dragged from the overflow shelf to the main shelf.
if (!dragged_to_another_shelf_) {
dragged_to_another_shelf_ = true;
drag_image_->SetOpacity(1.0f);
main_shelf_->StartDrag(dragged_app_id, screen_location);
} else {
main_shelf_->Drag(screen_location);
}
} else if (dragged_off_from_overflow_to_shelf_) {
} else if (!is_overflow_mode() && overflow_bubble_ &&
overflow_bubble_->IsShowing() &&
overflow_bubble_->shelf_view()
->GetBoundsForDragInsertInScreen()
.Contains(screen_location)) {
// The item was dragged from the main shelf to the overflow shelf.
if (!dragged_to_another_shelf_) {
dragged_to_another_shelf_ = true;
drag_image_->SetOpacity(1.0f);
overflow_bubble_->shelf_view()->StartDrag(dragged_app_id,
screen_location);
} else {
overflow_bubble_->shelf_view()->Drag(screen_location);
}
} else if (dragged_to_another_shelf_) {
// Makes the |drag_image_| partially disappear again.
dragged_off_from_overflow_to_shelf_ = false;
dragged_to_another_shelf_ = false;
drag_image_->SetOpacity(kDraggedImageOpacity);
main_shelf_->EndDrag(true);
EndDragOnOtherShelf(true /* cancel */);
if (!is_overflow_mode()) {
// During dragging, the position of the dragged item is moved to the
// back. If the overflow bubble is showing, a copy of the dragged item
// will appear at the end of the overflow shelf. Decrement the last
// visible index of the overflow shelf to hide this copy.
overflow_bubble_->shelf_view()->last_visible_index_--;
}
bounds_animator_->StopAnimatingView(drag_view_);
int drag_view_index = view_model_->GetIndexOfView(drag_view_);
drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
......@@ -1114,6 +1147,13 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
if (current_index != model_->FirstPanelIndex() - 1) {
model_->Move(current_index, model_->FirstPanelIndex() - 1);
StartFadeInLastVisibleItem();
// During dragging, the position of the dragged item is moved to the
// back. If the overflow bubble is showing, a copy of the dragged item
// will appear at the end of the overflow shelf. Decrement the last
// visible index of the overflow shelf to hide this copy.
if (overflow_bubble_ && overflow_bubble_->IsShowing())
overflow_bubble_->shelf_view()->last_visible_index_--;
} else if (is_overflow_mode()) {
// Overflow bubble should be shrunk when an item is ripped off.
PreferredSizeChanged();
......@@ -1148,9 +1188,9 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) {
bool snap_back = false;
// Items which cannot be dragged off will be handled as a cancel.
if (!cancel) {
if (dragged_off_from_overflow_to_shelf_) {
dragged_off_from_overflow_to_shelf_ = false;
main_shelf_->EndDrag(false);
if (dragged_to_another_shelf_) {
dragged_to_another_shelf_ = false;
EndDragOnOtherShelf(false /* cancel */);
drag_view_->layer()->SetOpacity(1.0f);
} else if (RemovableByRipOff(current_index) != REMOVABLE) {
// Make sure we do not try to remove un-removable items like items which
......@@ -1166,10 +1206,10 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) {
}
}
if (cancel || snap_back) {
if (dragged_off_from_overflow_to_shelf_) {
dragged_off_from_overflow_to_shelf_ = false;
// Main shelf handles revert of dragged item.
main_shelf_->EndDrag(true);
if (dragged_to_another_shelf_) {
dragged_to_another_shelf_ = false;
// Other shelf handles revert of dragged item.
EndDragOnOtherShelf(false /* true */);
drag_view_->layer()->SetOpacity(1.0f);
} else if (!cancelling_drag_model_changed_) {
// Only do something if the change did not come through a model change.
......@@ -1378,10 +1418,9 @@ gfx::Size ShelfView::GetPreferredSize() const {
// When an item is dragged off from the overflow bubble, it is moved to last
// position and and changed to invisible. Overflow bubble size should be
// shrunk to fit only for visible items.
// If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
// items in the shelf.
if (is_overflow_mode() && dragged_off_shelf_ &&
!dragged_off_from_overflow_to_shelf_ &&
// If |dragged_to_another_shelf_| is set, there will be no
// invisible items in the shelf.
if (is_overflow_mode() && dragged_off_shelf_ && !dragged_to_another_shelf_ &&
RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE)
last_button_index--;
......
......@@ -230,6 +230,11 @@ class ASH_EXPORT ShelfView : public views::View,
// Invoked when the mouse is dragged. Updates the models as appropriate.
void ContinueDrag(const ui::LocatedEvent& event);
// Ends the drag on the other shelf. (ie if we are on main shelf, ends drag on
// the overflow shelf). Invoked when a shelf item is being dragged from one
// shelf to the other.
void EndDragOnOtherShelf(bool cancel);
// Handles ripping off an item from the shelf. Returns true when the item got
// removed.
bool HandleRipOffDrag(const ui::LocatedEvent& event);
......@@ -446,6 +451,9 @@ class ASH_EXPORT ShelfView : public views::View,
// True when the icon was dragged off the shelf.
bool dragged_off_shelf_ = false;
// True when an item is dragged from one shelf to another (eg. overflow).
bool dragged_to_another_shelf_ = false;
// The rip off view when a snap back operation is underway.
views::View* snap_back_from_rip_off_view_ = nullptr;
......
......@@ -492,7 +492,13 @@ class ShelfViewTest : public AshTestBase {
}
}
void TestDraggingAnItemFromOverflowToShelf(bool cancel) {
// Helper function for testing dragging an item off one shelf to another
// shelf. |main_to_overflow| is true if we are moving the item from the main
// shelf to the overflow shelf; it is false if we are moving the item from the
// overflow shelf to the main shelf. |cancel| is true if we want to cancel the
// dragging halfway through.
void TestDraggingAnItemFromShelfToOtherShelf(bool main_to_overflow,
bool cancel) {
test_api_->ShowOverflowBubble();
ASSERT_TRUE(test_api_->IsShowingOverflowBubble());
......@@ -501,6 +507,9 @@ class ShelfViewTest : public AshTestBase {
int total_item_count = model_->item_count();
// Intialize some ids to test after the drag operation is canceled or
// completed. These ids are set assuming the both the main shelf and
// overflow shelf has more than 3 items.
ShelfID last_visible_item_id_in_shelf =
GetItemId(test_api_->GetLastVisibleIndex());
ShelfID second_last_visible_item_id_in_shelf =
......@@ -510,49 +519,62 @@ class ShelfViewTest : public AshTestBase {
ShelfID second_last_visible_item_id_in_overflow =
GetItemId(test_api_for_overflow.GetLastVisibleIndex() - 1);
int drag_item_index = test_api_for_overflow.GetLastVisibleIndex();
// |src_api| represents the test api of the shelf we are moving the item
// from. |dest_api| represents the test api of the shelf we are moving the
// item too.
ShelfViewTestAPI* src_api =
main_to_overflow ? test_api_.get() : &test_api_for_overflow;
ShelfViewTestAPI* dest_api =
main_to_overflow ? &test_api_for_overflow : test_api_.get();
// Set the item to be dragged depending on |main_to_overflow|.
int drag_item_index = main_to_overflow ? 1 : src_api->GetLastVisibleIndex();
ShelfID drag_item_id = GetItemId(drag_item_index);
ShelfButton* drag_button = test_api_for_overflow.GetButton(drag_item_index);
gfx::Point center_point_of_drag_item =
drag_button->GetBoundsInScreen().CenterPoint();
ShelfButton* drag_button = src_api->GetButton(drag_item_index);
gfx::Point center_point_of_drag_item = GetButtonCenter(drag_button);
ui::test::EventGenerator& generator = GetEventGenerator();
generator.set_current_location(center_point_of_drag_item);
// Rip an item off to OverflowBubble.
// Rip an item off this source shelf.
generator.PressLeftButton();
gfx::Point rip_off_point(center_point_of_drag_item.x(), 0);
generator.MoveMouseTo(rip_off_point);
test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
ASSERT_TRUE(test_api_for_overflow.IsRippedOffFromShelf());
ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
// Move a dragged item into Shelf at |drop_index|.
int drop_index = 1;
gfx::Point drop_point =
test_api_->GetButton(drop_index)->GetBoundsInScreen().CenterPoint();
// To insert at |drop_index|, more smaller x-axis value of |drop_point|
// should be used.
gfx::Point modified_drop_point(drop_point.x() - kShelfButtonSize / 4,
src_api->RunMessageLoopUntilAnimationsDone();
dest_api->RunMessageLoopUntilAnimationsDone();
ASSERT_TRUE(src_api->IsRippedOffFromShelf());
ASSERT_FALSE(src_api->DraggedItemToAnotherShelf());
// Move a dragged item into the destination shelf at |drop_index|.
int drop_index = main_to_overflow ? dest_api->GetLastVisibleIndex() : 1;
ShelfButton* drop_button = dest_api->GetButton(drop_index);
gfx::Point drop_point = GetButtonCenter(drop_button);
// To insert at |drop_index|, a smaller x-axis value of |drop_point|
// should be used. If |drop_index| is the last item, a larger x-axis
// value of |drop_point| should be used.
int drop_point_x_shift =
main_to_overflow ? kShelfButtonSize / 4 : -kShelfButtonSize / 4;
gfx::Point modified_drop_point(drop_point.x() + drop_point_x_shift,
drop_point.y());
generator.MoveMouseTo(modified_drop_point);
test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
test_api_->RunMessageLoopUntilAnimationsDone();
ASSERT_TRUE(test_api_for_overflow.IsRippedOffFromShelf());
ASSERT_TRUE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
src_api->RunMessageLoopUntilAnimationsDone();
dest_api->RunMessageLoopUntilAnimationsDone();
ASSERT_TRUE(src_api->IsRippedOffFromShelf());
ASSERT_TRUE(src_api->DraggedItemToAnotherShelf());
if (cancel)
drag_button->OnMouseCaptureLost();
else
generator.ReleaseLeftButton();
test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
test_api_->RunMessageLoopUntilAnimationsDone();
ASSERT_FALSE(test_api_for_overflow.IsRippedOffFromShelf());
ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
generator.ReleaseLeftButton();
src_api->RunMessageLoopUntilAnimationsDone();
dest_api->RunMessageLoopUntilAnimationsDone();
ASSERT_FALSE(src_api->IsRippedOffFromShelf());
ASSERT_FALSE(src_api->DraggedItemToAnotherShelf());
// Compare pre-stored items' id with newly positioned items' after dragging
// is canceled or finished.
if (cancel) {
// Item ids should remain unchanged if operation was canceled.
EXPECT_EQ(last_visible_item_id_in_shelf,
GetItemId(test_api_->GetLastVisibleIndex()));
EXPECT_EQ(second_last_visible_item_id_in_shelf,
......@@ -564,14 +586,42 @@ class ShelfViewTest : public AshTestBase {
} else {
EXPECT_EQ(drag_item_id, GetItemId(drop_index));
EXPECT_EQ(total_item_count, model_->item_count());
EXPECT_EQ(last_visible_item_id_in_shelf,
GetItemId(test_api_for_overflow.GetFirstVisibleIndex()));
EXPECT_EQ(second_last_visible_item_id_in_shelf,
GetItemId(test_api_->GetLastVisibleIndex()));
EXPECT_EQ(first_visible_item_id_in_overflow,
GetItemId(test_api_for_overflow.GetFirstVisibleIndex() + 1));
EXPECT_EQ(second_last_visible_item_id_in_overflow,
GetItemId(test_api_for_overflow.GetLastVisibleIndex()));
if (main_to_overflow) {
// If we move an item from the main shelf to the overflow shelf, the
// following should happen:
// 1) The former last item on the main shelf should now be the second
// last item on the main shelf.
// 2) The former first item on the overflow shelf should now be the last
// item on the main shelf.
// 3) The dragged item should now be the last item on the main shelf.
EXPECT_EQ(last_visible_item_id_in_shelf,
GetItemId(test_api_->GetLastVisibleIndex() - 1));
EXPECT_EQ(first_visible_item_id_in_overflow,
GetItemId(test_api_->GetLastVisibleIndex()));
EXPECT_EQ(drag_item_id,
GetItemId(test_api_for_overflow.GetLastVisibleIndex()));
} else {
// If we move an item from the overflow shelf to the main shelf, the
// following should happen:
// 1) The former last item on the main shelf should now be the first
// item on the overflow shelf.
// 2) The former second last item on the main shelf should now be the
// last item on the main shelf.
// 3) The former first item on the overflow shelf should now be the
// second item on the overflow shelf.
// 4) The former second item on the overflow shelf should now be the
// last item on the overflow shelf (since there are 3 items on the
// overflow shelf).
EXPECT_EQ(last_visible_item_id_in_shelf,
GetItemId(test_api_for_overflow.GetFirstVisibleIndex()));
EXPECT_EQ(second_last_visible_item_id_in_shelf,
GetItemId(test_api_->GetLastVisibleIndex()));
EXPECT_EQ(first_visible_item_id_in_overflow,
GetItemId(test_api_for_overflow.GetFirstVisibleIndex() + 1));
EXPECT_EQ(second_last_visible_item_id_in_overflow,
GetItemId(test_api_for_overflow.GetLastVisibleIndex()));
}
}
test_api_->HideOverflowBubble();
}
......@@ -1679,15 +1729,23 @@ TEST_F(ShelfViewTest, CheckRipOffFromLeftShelfAlignmentWithMultiMonitor) {
EXPECT_TRUE(test_api_for_secondary_shelf_view.IsRippedOffFromShelf());
}
// Checks various drag and drop operations from OverflowBubble to Shelf.
TEST_F(ShelfViewTest, CheckDragAndDropFromOverflowBubbleToShelf) {
// Checks various drag and drop operations from OverflowBubble to Shelf, and
// vice versa.
TEST_F(ShelfViewTest, CheckDragAndDropFromShelfToOtherShelf) {
AddButtonsUntilOverflow();
// Add one more button to prevent the overflow bubble to disappear upon
// dragging an item out on windows (flakiness, see crbug.com/425097).
AddAppShortcut();
TestDraggingAnItemFromOverflowToShelf(false);
TestDraggingAnItemFromOverflowToShelf(true);
TestDraggingAnItemFromShelfToOtherShelf(false /* main_to_overflow */,
false /* cancel */);
TestDraggingAnItemFromShelfToOtherShelf(false /* main_to_overflow */,
true /* cancel */);
TestDraggingAnItemFromShelfToOtherShelf(true /* main_to_overflow */,
false /* cancel */);
TestDraggingAnItemFromShelfToOtherShelf(true /* main_to_overflow */,
true /* cancel */);
}
// Checks creating app shortcut for an opened platform app in overflow bubble
......
......@@ -158,8 +158,8 @@ bool ShelfViewTestAPI::IsRippedOffFromShelf() {
return shelf_view_->dragged_off_shelf_;
}
bool ShelfViewTestAPI::DraggedItemFromOverflowToShelf() {
return shelf_view_->dragged_off_from_overflow_to_shelf_;
bool ShelfViewTestAPI::DraggedItemToAnotherShelf() {
return shelf_view_->dragged_to_another_shelf_;
}
ShelfButtonPressedMetricTracker*
......
......@@ -112,8 +112,9 @@ class ShelfViewTestAPI {
// Returns true if item is ripped off.
bool IsRippedOffFromShelf();
// Returns true if an item is ripped off and entered into shelf.
bool DraggedItemFromOverflowToShelf();
// Returns true when an item is dragged from one shelf to another (eg.
// overflow).
bool DraggedItemToAnotherShelf();
// An accessor for |shelf_button_pressed_metric_tracker_|.
ShelfButtonPressedMetricTracker* shelf_button_pressed_metric_tracker();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment