Commit e17da90f authored by Wen-Chien Wang's avatar Wen-Chien Wang Committed by Commit Bot

Add a feature of dragging an unpinned open app to pin in shelf

Previously the only way to pin an unpinned open app in shelf is to use the context menu. In this update, we enable the feature that dragging an unpinned open app to the pinned app side is available to pin the app.

Bug: 1084856

Change-Id: I559a8967942a1c4d6ee7eb370cd81181b173c361
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2321065Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarToni Baržić <tbarzic@chromium.org>
Reviewed-by: default avatarJenny Zhang <jennyz@chromium.org>
Commit-Queue: Wen-Chien Wang <wcwang@google.com>
Cr-Commit-Position: refs/heads/master@{#798198}
parent 2046880f
......@@ -148,6 +148,9 @@ const base::Feature kMaintainShelfStateWhenEnteringOverview{
const base::Feature kTemporaryHoldingSpace{"TemporaryHoldingSpace",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kDragUnpinnedAppToPin{"DragUnpinnedAppToPin",
base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAllowAmbientEQEnabled() {
return base::FeatureList::IsEnabled(kAllowAmbientEQ);
}
......@@ -303,6 +306,10 @@ bool IsTemporaryHoldingSpaceEnabled() {
return base::FeatureList::IsEnabled(kTemporaryHoldingSpace);
}
bool IsDragUnpinnedAppToPinEnabled() {
return base::FeatureList::IsEnabled(kDragUnpinnedAppToPin);
}
namespace {
// The boolean flag indicating if "WebUITabStrip" feature is enabled in Chrome.
......
......@@ -163,6 +163,9 @@ ASH_PUBLIC_EXPORT extern const base::Feature
// later.
ASH_PUBLIC_EXPORT extern const base::Feature kTemporaryHoldingSpace;
// Enables dragging an unpinned open app to pinned app side to pin.
ASH_PUBLIC_EXPORT extern const base::Feature kDragUnpinnedAppToPin;
ASH_PUBLIC_EXPORT bool IsAllowAmbientEQEnabled();
ASH_PUBLIC_EXPORT bool IsAltTabLimitedToActiveDesk();
......@@ -227,6 +230,8 @@ ASH_PUBLIC_EXPORT bool IsMaintainShelfStateWhenEnteringOverviewEnabled();
ASH_PUBLIC_EXPORT bool IsTemporaryHoldingSpaceEnabled();
ASH_PUBLIC_EXPORT bool IsDragUnpinnedAppToPinEnabled();
// These two functions are supposed to be temporary functions to set or get
// whether "WebUITabStrip" feature is enabled from Chrome.
ASH_PUBLIC_EXPORT void SetWebUITabStripEnabled(bool enabled);
......
......@@ -962,13 +962,17 @@ void ShelfView::CalculateIdealBounds() {
DCHECK(model()->item_count() == view_model_->view_size());
const int button_spacing = ShelfConfig::Get()->button_spacing();
const int separator_index = GetSeparatorIndex();
UpdateSeparatorIndex();
const int hotseat_size = shelf_->hotseat_widget()->GetHotseatSize();
// Don't show the separator if it isn't needed, or would appear after all
// visible items.
separator_->SetVisible(separator_index != -1 &&
separator_index < visible_views_indices_.back());
separator_->SetVisible(separator_index_ != -1 &&
separator_index_ < visible_views_indices_.back());
// Set |separator_index_| to -1 if it is not visible.
if (!separator_->GetVisible())
separator_index_ = -1;
app_icons_layout_offset_ = CalculateAppIconsLayoutOffset();
int x = shelf()->PrimaryAxisValue(app_icons_layout_offset_, 0);
......@@ -981,9 +985,9 @@ void ShelfView::CalculateIdealBounds() {
const bool is_visible = view_model_->view_at(i)->GetVisible();
if (!is_visible) {
// Layout hidden views with empty bounds so they don't consume horizontal
// space. Note that |separator_index| cannot be the index of a hidden
// space. Note that |separator_index_| cannot be the index of a hidden
// view.
DCHECK_NE(i, separator_index);
DCHECK_NE(i, separator_index_);
view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
continue;
}
......@@ -993,7 +997,7 @@ void ShelfView::CalculateIdealBounds() {
x = shelf()->PrimaryAxisValue(x + button_size + button_spacing, x);
y = shelf()->PrimaryAxisValue(y, y + button_size + button_spacing);
if (i == separator_index) {
if (i == separator_index_) {
// Place the separator halfway between the two icons it separates,
// vertically centered.
int half_space = button_spacing / 2;
......@@ -1040,20 +1044,54 @@ int ShelfView::GetAvailableSpaceForAppIcons() const {
return shelf()->PrimaryAxisValue(width(), height());
}
int ShelfView::GetSeparatorIndex() const {
void ShelfView::UpdateSeparatorIndex() {
// A separator is shown after the last pinned item only if it's followed by a
// visible app item.
int next_visible_app_item_index = -1;
int first_unpinned_index = -1;
int last_pinned_index = -1;
int dragged_item_index = -1;
if (drag_view_)
dragged_item_index = view_model_->GetIndexOfView(drag_view_);
const bool can_drag_view_across_separator =
drag_view_ && CanDragAcrossSeparator(drag_view_);
for (int i = model()->item_count() - 1; i >= 0; --i) {
const auto& item = model()->items()[i];
if (IsItemPinned(item))
return next_visible_app_item_index != -1 ? i : -1;
if (IsItemPinned(item)) {
// Dragged pinned item may be moved to the unpinned side of the shelf and
// may end up right of an unpinned app. Dismisses the dragged item to
// check the next one.
if (i == dragged_item_index && can_drag_view_across_separator)
continue;
last_pinned_index = i;
break;
}
if (item.type == TYPE_APP && item.is_on_active_desk)
next_visible_app_item_index = i;
first_unpinned_index = i;
}
return -1;
// If there is no unpinned item in shelf, return -1 as the separator should be
// hidden.
if (first_unpinned_index == -1) {
separator_index_ = -1;
return;
}
// If the dragged item is between the pinned apps and unpinned apps, move it
// to the pinned app side if it is closer to the pinned section compared to
// its ideal bounds.
if (can_drag_view_across_separator &&
last_pinned_index < dragged_item_index &&
dragged_item_index <= first_unpinned_index &&
drag_view_relative_to_ideal_bounds_ == RelativePosition::kLeft) {
separator_index_ = dragged_item_index;
return;
}
separator_index_ = last_pinned_index;
}
void ShelfView::DestroyDragIconProxy() {
......@@ -1263,6 +1301,21 @@ void ShelfView::PointerReleasedOnButton(views::View* view,
} else if (drag_pointer_ == pointer) {
FinalizeRipOffDrag(false);
drag_pointer_ = NONE;
// Check if the pin status of |drag_view_| should be changed when
// |drag_view_| is dragged over the separator. Do nothing if |drag_view_| is
// already handled in FinalizedRipOffDrag.
if (drag_view_) {
if (ShouldUpdateDraggedViewPinStatus(view_model_->GetIndexOfView(view))) {
const std::string drag_app_id = ShelfItemForView(drag_view_)->id.app_id;
ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
if (model_->IsAppPinned(drag_app_id)) {
model_->UnpinAppWithID(drag_app_id);
} else {
model_->PinAppWithID(drag_app_id);
}
}
}
AnimateToIdealBounds();
}
......@@ -1272,8 +1325,9 @@ void ShelfView::PointerReleasedOnButton(views::View* view,
drag_and_drop_host_->DestroyDragIconProxy();
// If the drag pointer is NONE, no drag operation is going on and the
// drag_view can be released.
// |drag_view_| can be released.
drag_view_ = nullptr;
drag_view_relative_to_ideal_bounds_ = RelativePosition::kNotAvailable;
}
void ShelfView::LayoutToIdealBounds() {
......@@ -1400,27 +1454,25 @@ void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
}
void ShelfView::MoveDragViewTo(int primary_axis_coordinate) {
const int current_index = view_model_->GetIndexOfView(drag_view_);
const std::pair<int, int> indices(GetDragRange(current_index));
const int current_item_index = view_model_->GetIndexOfView(drag_view_);
const std::pair<int, int> indices(GetDragRange(current_item_index));
if (shelf_->IsHorizontalAlignment()) {
int x = GetMirroredXWithWidthInView(primary_axis_coordinate,
drag_view_->width());
x = std::max(view_model_->ideal_bounds(indices.first).x(), x);
x = std::min(view_model_->ideal_bounds(indices.second).right() -
view_model_->ideal_bounds(current_index).width(),
view_model_->ideal_bounds(current_item_index).width(),
x);
if (drag_view_->x() == x)
return;
drag_view_->SetX(x);
if (drag_view_->x() != x)
drag_view_->SetX(x);
} else {
int y = std::max(view_model_->ideal_bounds(indices.first).y(),
primary_axis_coordinate);
y = std::min(view_model_->ideal_bounds(indices.second).bottom() -
view_model_->ideal_bounds(current_index).height(),
view_model_->ideal_bounds(current_item_index).height(),
y);
if (drag_view_->y() == y)
return;
drag_view_->SetY(y);
if (drag_view_->y() != y)
drag_view_->SetY(y);
}
int target_index = views::ViewModelUtils::DetermineMoveIndex(
......@@ -1429,12 +1481,36 @@ void ShelfView::MoveDragViewTo(int primary_axis_coordinate) {
target_index =
base::ClampToRange(target_index, indices.first, indices.second);
if (target_index == current_index)
return;
// Check the relative position of |drag_view_| and its ideal bounds if it can
// be dragged across the separator to pin or unpin.
if (CanDragAcrossSeparator(drag_view_)) {
// Compare the center points of |drag_view_| and its ideal bounds to
// determine whether the separator should be moved to the left or right by
// using |drag_view_relative_to_ideal_bounds_|. The actual position will
// be updated in CalculateIdealBounds.
gfx::Point drag_view_center = drag_view_->bounds().CenterPoint();
int drag_view_position =
shelf()->PrimaryAxisValue(drag_view_center.x(), drag_view_center.y());
gfx::Point ideal_bound_center =
view_model_->ideal_bounds(target_index).CenterPoint();
int ideal_bound_position = shelf()->PrimaryAxisValue(
ideal_bound_center.x(), ideal_bound_center.y());
drag_view_relative_to_ideal_bounds_ =
drag_view_position < ideal_bound_position ? RelativePosition::kLeft
: RelativePosition::kRight;
if (target_index == current_item_index) {
AnimateToIdealBounds();
NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
true /* send_native_event */);
}
}
// Change the model, the ShelfItemMoved() callback will handle the
// |view_model_| update.
model_->Move(current_index, target_index);
if (target_index == current_item_index)
return;
// Change the model if the dragged item index is changed, the ShelfItemMoved()
// callback will handle the |view_model_| update.
model_->Move(current_item_index, target_index);
bounds_animator_->StopAnimatingView(drag_view_);
}
......@@ -1626,12 +1702,19 @@ bool ShelfView::ShouldFocusOut(bool reverse, views::View* button) {
std::pair<int, int> ShelfView::GetDragRange(int index) {
DCHECK(base::Contains(visible_views_indices_, index));
const ShelfItemType type = model_->items()[index].type;
const ShelfItem& dragged_item = model_->items()[index];
// If |drag_view_| is allowed to be dragged across the separator, return the
// first and the last index of the |visible_views_indices_|.
if (CanDragAcrossSeparator(drag_view_)) {
return std::make_pair(visible_views_indices_[0],
visible_views_indices_.back());
}
int first = -1;
int last = -1;
for (int i : visible_views_indices_) {
if (SameDragType(model_->items()[i].type, type)) {
if (SameDragType(model_->items()[i].type, dragged_item.type)) {
if (first == -1)
first = i;
last = i;
......@@ -1647,6 +1730,50 @@ std::pair<int, int> ShelfView::GetDragRange(int index) {
return std::make_pair(first, last);
}
bool ShelfView::ShouldUpdateDraggedViewPinStatus(int dragged_view_index) {
if (!features::IsDragUnpinnedAppToPinEnabled())
return false;
DCHECK(base::Contains(visible_views_indices_, dragged_view_index));
bool is_moved_item_pinned =
IsPinnedShelfItemType(model_->items()[dragged_view_index].type);
if (separator_index_ == -1) {
// If |separator_index_| equals to -1, all the apps in shelf are expected to
// have the same pinned status.
for (auto index : visible_views_indices_) {
if (index != dragged_view_index) {
// Return true if the pin status of the moved item is different from
// others.
return is_moved_item_pinned !=
IsPinnedShelfItemType(model_->items()[index].type);
}
}
return false;
}
// If the separator is shown, check whether the pin status of dragged item
// matches the pin status implied by the dragged view position relative to the
// separator.
bool should_pinned_by_position = dragged_view_index <= separator_index_;
return should_pinned_by_position != is_moved_item_pinned;
}
bool ShelfView::CanDragAcrossSeparator(views::View* drag_view) const {
if (!features::IsDragUnpinnedAppToPinEnabled())
return false;
DCHECK(drag_view);
// The dragged item is not allowed to be unpinned if |drag_view| is pinned by
// policy, dragged from app list, or its item type is TYPE_BROWSER_SHORTCUT.
// Therefore, the |drag_view| can not be dragged across the separator.
bool can_change_pin_state =
ShelfItemForView(drag_view)->type == TYPE_PINNED_APP ||
ShelfItemForView(drag_view)->type == TYPE_APP;
// Note that |drag_and_drop_shelf_id_| is set only when the current drag view
// is from app list, which can not be dragged to the unpinned app side.
return !ShelfItemForView(drag_view)->pinned_by_policy &&
drag_and_drop_shelf_id_ == ShelfID() && can_change_pin_state;
}
void ShelfView::OnFadeInAnimationEnded() {
// Call PreferredSizeChanged() to notify container to re-layout at the end
// of fade-in animation.
......
......@@ -337,9 +337,8 @@ class ASH_EXPORT ShelfView : public views::AccessiblePaneView,
// not available for app icons.
int GetAvailableSpaceForAppIcons() const;
// Returns the index of the item after which the separator should be shown,
// or -1 if no separator is required.
int GetSeparatorIndex() const;
// Updates the index of the separator and save it to |separator_index_|.
void UpdateSeparatorIndex();
// Sets the bounds of each view to its ideal bounds.
void LayoutToIdealBounds();
......@@ -406,6 +405,15 @@ class ASH_EXPORT ShelfView : public views::AccessiblePaneView,
// dragged to.
std::pair<int, int> GetDragRange(int index);
// Checks if the item at |dragged_item_index| should be pinned or unpinned on
// pointer release.
bool ShouldUpdateDraggedViewPinStatus(int dragged_item_index);
// Checks if |dragged_view| is allowed to be dragged across the separator to
// perform pinning and unpinning. Note that this function doesn't check if the
// separator exists.
bool CanDragAcrossSeparator(views::View* dragged_view) const;
// If there is a drag operation in progress it's canceled. If |modified_index|
// is valid, the new position of the corresponding item is returned.
int CancelDrag(int modified_index);
......@@ -553,6 +561,25 @@ class ASH_EXPORT ShelfView : public views::AccessiblePaneView,
// items.
views::Separator* separator_ = nullptr;
// Index of |separator_|. It is set to -1 if it is invisible.
int separator_index_ = -1;
// Used in |drag_view_relative_to_ideal_bounds_| to represent the relative
// position between |drag_view_| and its ideal bounds in shelf.
enum class RelativePosition {
// Set if |drag_view_| is not available or the relative position is not
// calculated yet.
kNotAvailable,
// Set if |drag_view_| is to the left of its ideal bounds.
kLeft,
// Set if |drag_view_| is to the right of its ideal bounds.
kRight
};
// The |drag_view_|'s current position relative to its ideal bounds.
RelativePosition drag_view_relative_to_ideal_bounds_ =
RelativePosition::kNotAvailable;
// Position of the mouse down event in |drag_view_|'s coordinates.
gfx::Point drag_origin_;
......
......@@ -13,6 +13,7 @@
#include "base/run_loop.h"
#include "ui/views/animation/bounds_animator.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/separator.h"
#include "ui/views/view_model.h"
namespace {
......@@ -153,4 +154,12 @@ void ShelfViewTestAPI::SetShelfContextMenuCallback(
shelf_view_->context_menu_shown_callback_ = std::move(closure);
}
int ShelfViewTestAPI::GetSeparatorIndex() const {
return shelf_view_->separator_index_;
}
bool ShelfViewTestAPI::IsSeparatorVisible() const {
return shelf_view_->separator_->GetVisible();
}
} // namespace ash
......@@ -113,6 +113,12 @@ class ShelfViewTestAPI {
// Set callback which will run after showing shelf context menu.
void SetShelfContextMenuCallback(base::RepeatingClosure closure);
// Returns |separator_index_|.
int GetSeparatorIndex() const;
// Checks whether the separator is visible or not.
bool IsSeparatorVisible() const;
private:
ShelfView* shelf_view_;
int id_ = 0;
......
......@@ -402,7 +402,6 @@ class ShelfViewTest : public AshTestBase {
ShelfItem item = model_->items()[model_index];
ShelfID id = item.id;
EXPECT_EQ(id_map[map_index].first, id);
EXPECT_EQ(id_map[map_index].second, GetButtonByID(id));
++map_index;
}
ASSERT_EQ(map_index, id_map.size());
......@@ -622,6 +621,17 @@ class ShelfViewTextDirectionTest : public ShelfViewTest,
DISALLOW_COPY_AND_ASSIGN(ShelfViewTextDirectionTest);
};
class ShelfViewDragToPinTest : public ShelfViewTest {
public:
ShelfViewDragToPinTest() {
feature_list_.InitAndEnableFeature(features::kDragUnpinnedAppToPin);
}
~ShelfViewDragToPinTest() override = default;
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(ShelfViewTest, VisibleShelfItemsBounds) {
// Add 3 pinned apps, and a normal app.
AddAppShortcut();
......@@ -734,6 +744,153 @@ TEST_F(ShelfViewTest, SimultaneousDrag) {
ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
}
// Ensure that the behavior of pinning and unpinning by dragging works as
// expected.
TEST_F(ShelfViewDragToPinTest, DragAppsToPinAndUnpin) {
std::vector<std::pair<ShelfID, views::View*>> id_map;
SetupForDragTest(&id_map);
int pinned_apps_size = id_map.size();
const ShelfID open_app_id = AddApp();
id_map.push_back(std::make_pair(open_app_id, GetButtonByID(open_app_id)));
// Run the pinned app at index 1.
ShelfItem item = model_->items()[1];
item.status = STATUS_RUNNING;
model_->Set(1, item);
id_map[1].second = GetButtonByID(item.id);
ASSERT_TRUE(static_cast<ShelfAppButton*>(id_map[1].second)->state() &
ShelfAppButton::STATE_RUNNING);
ASSERT_FALSE(static_cast<ShelfAppButton*>(id_map[2].second)->state() &
ShelfAppButton::STATE_RUNNING);
ASSERT_TRUE(GetButtonByID(open_app_id)->state() &
ShelfAppButton::STATE_RUNNING);
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
// Drag the app at index 1 and move it to the end of the shelf. With separator
// available and the app is dragged to the unpinned app side, the dragged open
// app should be unpinned and moved to the released position.
views::View* dragged_button =
SimulateDrag(ShelfView::MOUSE, 1, id_map.size() - 1, false);
std::rotate(id_map.begin() + 1, id_map.begin() + 2, id_map.end());
ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
EXPECT_FALSE(IsAppPinned(id_map.back().first));
--pinned_apps_size;
// Drag the app at index 2 and move it to the end of the shelf. With separator
// available and the app is dragged to the unpinned app side, the dragged app
// with no running instance should be unpinned and removed from shelf.
dragged_button = SimulateDrag(ShelfView::MOUSE, 2, id_map.size() - 1, false);
shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
id_map.erase(id_map.begin() + 2);
EXPECT_EQ(id_map.size(), model_->items().size());
ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
--pinned_apps_size;
// Drag an app in unpinned app side and move it to the beginning of the shelf.
// With separator available and the app is dragged to the pinned app side, the
// dragged app should be pinned and moved to the released position.
dragged_button = SimulateDrag(ShelfView::MOUSE, id_map.size() - 2, 0, false);
std::rotate(id_map.rbegin() + 1, id_map.rbegin() + 2, id_map.rend());
ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
EXPECT_TRUE(IsAppPinned(id_map[0].first));
++pinned_apps_size;
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
}
// Check that the Browser Shortcut will not be dragged to the unpinned app side
// or be unpinned by dragging.
TEST_F(ShelfViewDragToPinTest, BlockBrowserShortcutFromUnpinningByDragging) {
std::vector<std::pair<ShelfID, views::View*>> id_map;
SetupForDragTest(&id_map);
const int pinned_apps_size = id_map.size();
const ShelfID open_app_id = AddApp();
id_map.push_back(std::make_pair(open_app_id, GetButtonByID(open_app_id)));
EXPECT_TRUE(model_->items()[0].type == TYPE_BROWSER_SHORTCUT);
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
// Dragging the browser shortcut to the unpinned app side should not make it
// unpinned.
views::View* dragged_button =
SimulateDrag(ShelfView::MOUSE, 0, id_map.size() - 1, false);
std::rotate(id_map.begin(), id_map.begin() + 1,
id_map.begin() + pinned_apps_size);
ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
// When drag pointer released, the browser shortcut should be the last app in
// the pinned app side.
shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
EXPECT_EQ(model_->items()[pinned_apps_size - 1].type, TYPE_BROWSER_SHORTCUT);
}
// Check that separator index updates as expected when a drag view is dragged
// over it.
TEST_F(ShelfViewDragToPinTest, DragAppAroundSeparator) {
std::vector<std::pair<ShelfID, views::View*>> id_map;
SetupForDragTest(&id_map);
const int pinned_apps_size = id_map.size();
const ShelfID open_app_id = AddApp();
id_map.push_back(std::make_pair(open_app_id, GetButtonByID(open_app_id)));
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
const int button_width =
GetButtonByID(open_app_id)->GetBoundsInScreen().width();
ui::test::EventGenerator* generator = GetEventGenerator();
// The test makes some assumptions that the shelf is bottom aligned.
ASSERT_EQ(shelf_view_->shelf()->alignment(), ShelfAlignment::kBottom);
// Drag an unpinned open app that is beside the separator around and check
// that the separator is correctly placed.
ASSERT_EQ(model_->ItemIndexByID(open_app_id),
test_api_->GetSeparatorIndex() + 1);
gfx::Point unpinned_app_location =
GetButtonCenter(GetButtonByID(open_app_id));
generator->set_current_screen_location(unpinned_app_location);
generator->PressLeftButton();
// Drag the mouse slightly to the left. The dragged app will stay at the same
// index but the separator will move to the right.
generator->MoveMouseBy(-button_width / 4, 0);
// In this case, the separator is moved to the end of the shelf so it is set
// invisible and the |separator_index_| will be updated to -1.
EXPECT_FALSE(test_api_->IsSeparatorVisible());
EXPECT_EQ(test_api_->GetSeparatorIndex(), -1);
// Drag the mouse slightly to the right where the dragged app will stay at the
// same index.
generator->MoveMouseBy(button_width / 2, 0);
// In this case, because the dragged app is not released or pinned yet,
// dragging it back to its original place will show the separator again.
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
generator->ReleaseLeftButton();
// Drag an pinned app that is beside the separator around and check that the
// separator is correctly placed. Check that the dragged app is not a browser
// shortcut, which can not be dragged across the separator.
ASSERT_NE(model_->items()[pinned_apps_size - 1].type, TYPE_BROWSER_SHORTCUT);
ASSERT_EQ(model_->ItemIndexByID(id_map[pinned_apps_size - 1].first),
test_api_->GetSeparatorIndex());
gfx::Point pinned_app_location =
GetButtonCenter(id_map[pinned_apps_size - 1].first);
generator->set_current_screen_location(pinned_app_location);
generator->PressLeftButton();
// Drag the mouse slightly to the right. The dragged app will stay at the same
// index but the separator will move to the left.
generator->MoveMouseBy(button_width / 4, 0);
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 2);
// Drag the mouse slightly to the left. The dragged app will stay at the same
// index but the separator will move to the right.
generator->MoveMouseBy(-button_width / 2, 0);
EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
generator->ReleaseLeftButton();
}
// Ensure that clicking on one item and then dragging another works as expected.
TEST_F(ShelfViewTest, ClickOneDragAnother) {
std::vector<std::pair<ShelfID, views::View*>> id_map;
......
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