Commit 7f2b378c authored by Richard Chui's avatar Richard Chui Committed by Commit Bot

Capture Mode: Add cursor states for Region Mode

Change the cursor type based on capture mode state and cursor location
in screen.

Test: manual, added test
Bug: 1135700
Change-Id: I57af8b672bf6f0e04a422ed5be4fe40468fcc3ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454838
Commit-Queue: Richard Chui <richui@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarSammie Quon <sammiequon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818591}
parent 964ae8f4
...@@ -177,6 +177,69 @@ aura::Window* GetPreferredRootWindow() { ...@@ -177,6 +177,69 @@ aura::Window* GetPreferredRootWindow() {
} // namespace } // namespace
class CaptureModeSession::ScopedCursorSetter {
public:
explicit ScopedCursorSetter(ui::mojom::CursorType cursor)
: cursor_manager_(Shell::Get()->cursor_manager()),
original_cursor_(cursor_manager_->GetCursor()),
original_cursor_visible_(cursor_manager_->IsCursorVisible()),
original_cursor_locked_(cursor_manager_->IsCursorLocked()) {
UpdateCursor(cursor);
}
ScopedCursorSetter(const ScopedCursorSetter&) = delete;
ScopedCursorSetter& operator=(const ScopedCursorSetter&) = delete;
~ScopedCursorSetter() {
// Only unlock the cursor if it wasn't locked before.
if (original_cursor_locked_)
return;
cursor_manager_->UnlockCursor();
cursor_manager_->SetCursor(original_cursor_);
if (original_cursor_visible_) {
cursor_manager_->ShowCursor();
} else {
cursor_manager_->HideCursor();
}
}
// Note that this will always make the cursor visible if it is not |kNone|.
void UpdateCursor(ui::mojom::CursorType cursor) {
if (original_cursor_locked_)
return;
const bool currently_locked = cursor_manager_->IsCursorLocked();
if (cursor_manager_->GetCursor().type() == cursor &&
cursor_manager_->IsCursorVisible()) {
if (!currently_locked)
cursor_manager_->LockCursor();
return;
}
if (currently_locked)
cursor_manager_->UnlockCursor();
if (cursor == ui::mojom::CursorType::kNone) {
cursor_manager_->HideCursor();
} else {
cursor_manager_->SetCursor(cursor);
cursor_manager_->ShowCursor();
}
cursor_manager_->LockCursor();
}
bool IsCursorVisible() const { return cursor_manager_->IsCursorVisible(); }
private:
wm::CursorManager* const cursor_manager_;
const gfx::NativeCursor original_cursor_;
const bool original_cursor_visible_;
// If the original cursor is already locked, don't make any changes to it.
const bool original_cursor_locked_;
};
CaptureModeSession::CaptureModeSession(CaptureModeController* controller) CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
: controller_(controller), : controller_(controller),
current_root_(GetPreferredRootWindow()), current_root_(GetPreferredRootWindow()),
...@@ -201,6 +264,10 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller) ...@@ -201,6 +264,10 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
UpdateCaptureLabelWidget(); UpdateCaptureLabelWidget();
RefreshStackingOrder(parent); RefreshStackingOrder(parent);
if (controller_->source() == CaptureModeSource::kRegion) {
cursor_setter_ =
std::make_unique<ScopedCursorSetter>(ui::mojom::CursorType::kCell);
}
if (controller_->source() == CaptureModeSource::kWindow) { if (controller_->source() == CaptureModeSource::kWindow) {
capture_window_observer_ = capture_window_observer_ =
std::make_unique<CaptureWindowObserver>(this, controller_->type()); std::make_unique<CaptureWindowObserver>(this, controller_->type());
...@@ -235,6 +302,16 @@ void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) { ...@@ -235,6 +302,16 @@ void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) {
capture_window_observer_.reset(); capture_window_observer_.reset();
} }
if (new_source == CaptureModeSource::kRegion) {
cursor_setter_ = std::make_unique<ScopedCursorSetter>(
capture_mode_bar_widget_.GetWindowBoundsInScreen().Contains(
previous_location_in_root_)
? ui::mojom::CursorType::kPointer
: ui::mojom::CursorType::kCell);
} else {
cursor_setter_.reset();
}
capture_mode_bar_view_->OnCaptureSourceChanged(new_source); capture_mode_bar_view_->OnCaptureSourceChanged(new_source);
UpdateDimensionsLabelWidget(/*is_resizing=*/false); UpdateDimensionsLabelWidget(/*is_resizing=*/false);
layer()->SchedulePaint(layer()->bounds()); layer()->SchedulePaint(layer()->bounds());
...@@ -484,10 +561,13 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -484,10 +561,13 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
} }
DCHECK_EQ(CaptureModeSource::kRegion, capture_source); DCHECK_EQ(CaptureModeSource::kRegion, capture_source);
DCHECK(cursor_setter_);
// Let the capture button handle any events it can handle first. // Let the capture button handle any events it can handle first.
if (ShouldCaptureLabelHandleEvent(event_target)) if (ShouldCaptureLabelHandleEvent(event_target)) {
cursor_setter_->UpdateCursor(ui::mojom::CursorType::kHand);
return; return;
}
// Allow events that are located on the capture mode bar to pass through so we // Allow events that are located on the capture mode bar to pass through so we
// can click the buttons. // can click the buttons.
...@@ -513,26 +593,23 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -513,26 +593,23 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
SetMouseWarpEnabled(*old_mouse_warp_status_); SetMouseWarpEnabled(*old_mouse_warp_status_);
old_mouse_warp_status_.reset(); old_mouse_warp_status_.reset();
OnLocatedEventReleased(location); OnLocatedEventReleased(location, is_event_on_capture_bar);
break;
case ui::ET_MOUSE_MOVED:
if (cursor_setter_->IsCursorVisible()) {
cursor_setter_->UpdateCursor(GetCursorType(
GetFineTunePosition(location, is_touch), is_event_on_capture_bar));
}
break; break;
default: default:
break; break;
} }
} }
void CaptureModeSession::OnLocatedEventPressed( FineTunePosition CaptureModeSession::GetFineTunePosition(
const gfx::Point& location_in_root, const gfx::Point& location_in_root,
bool is_touch, bool is_touch) const {
bool is_event_on_capture_bar) { // In the case of overlapping affordances, prioritize the bottomm right
initial_location_in_root_ = location_in_root;
previous_location_in_root_ = location_in_root;
if (is_selecting_region_)
return;
// Calculate the position and anchor points of the current pressed event.
fine_tune_position_ = FineTunePosition::kNone;
// In the case of overlapping affordances, prioritize the bottom right
// corner, then the rest of the corners, then the edges. // corner, then the rest of the corners, then the edges.
static const std::vector<FineTunePosition> drag_positions = { static const std::vector<FineTunePosition> drag_positions = {
FineTunePosition::kBottomRight, FineTunePosition::kBottomLeft, FineTunePosition::kBottomRight, FineTunePosition::kBottomLeft,
...@@ -551,29 +628,75 @@ void CaptureModeSession::OnLocatedEventPressed( ...@@ -551,29 +628,75 @@ void CaptureModeSession::OnLocatedEventPressed(
// both x and y, then |position| is the current pressed down affordance. // both x and y, then |position| is the current pressed down affordance.
if ((position_location - location_in_root).LengthSquared() <= if ((position_location - location_in_root).LengthSquared() <=
hit_radius_squared) { hit_radius_squared) {
fine_tune_position_ = position; return position;
MaybeShowMagnifierGlassAtPoint(position_location);
break;
} }
} }
if (fine_tune_position_ == FineTunePosition::kNone) { if (controller_->user_capture_region().Contains(location_in_root))
return FineTunePosition::kCenter;
return FineTunePosition::kNone;
}
ui::mojom::CursorType CaptureModeSession::GetCursorType(
FineTunePosition position,
bool is_event_on_capture_bar) const {
if (is_event_on_capture_bar ||
controller_->source() != CaptureModeSource::kRegion) {
return ui::mojom::CursorType::kPointer;
}
switch (position) {
case FineTunePosition::kTopLeft:
return ui::mojom::CursorType::kNorthWestResize;
case FineTunePosition::kBottomRight:
return ui::mojom::CursorType::kSouthEastResize;
case FineTunePosition::kTopCenter:
case FineTunePosition::kBottomCenter:
return ui::mojom::CursorType::kNorthSouthResize;
case FineTunePosition::kTopRight:
return ui::mojom::CursorType::kNorthEastResize;
case FineTunePosition::kBottomLeft:
return ui::mojom::CursorType::kSouthWestResize;
case FineTunePosition::kLeftCenter:
case FineTunePosition::kRightCenter:
return ui::mojom::CursorType::kEastWestResize;
case FineTunePosition::kCenter:
return ui::mojom::CursorType::kMove;
default:
return ui::mojom::CursorType::kCell;
}
}
void CaptureModeSession::OnLocatedEventPressed(
const gfx::Point& location_in_root,
bool is_touch,
bool is_event_on_capture_bar) {
initial_location_in_root_ = location_in_root;
previous_location_in_root_ = location_in_root;
if (is_selecting_region_)
return;
fine_tune_position_ = GetFineTunePosition(location_in_root, is_touch);
if (fine_tune_position_ == FineTunePosition::kNone &&
!is_event_on_capture_bar) {
// If the point is outside the capture region and not on the capture bar, // If the point is outside the capture region and not on the capture bar,
// restart to the select phase. // restart to the select phase.
if (controller_->user_capture_region().Contains(location_in_root)) { is_selecting_region_ = true;
fine_tune_position_ = FineTunePosition::kCenter; UpdateCaptureRegion(gfx::Rect(), /*is_resizing=*/true);
} else {
gfx::Point location_in_screen = location_in_root;
wm::ConvertPointToScreen(current_root_, &location_in_screen);
if (!is_event_on_capture_bar) {
is_selecting_region_ = true;
UpdateCaptureRegion(gfx::Rect(), /*is_resizing=*/true);
}
}
return; return;
} }
anchor_points_ = GetAnchorPointsForPosition(fine_tune_position_); if (fine_tune_position_ != FineTunePosition::kCenter &&
fine_tune_position_ != FineTunePosition::kNone) {
anchor_points_ = GetAnchorPointsForPosition(fine_tune_position_);
const gfx::Point position_location =
capture_mode_util::GetLocationForFineTunePosition(
controller_->user_capture_region(), fine_tune_position_);
MaybeShowMagnifierGlassAtPoint(position_location);
}
} }
void CaptureModeSession::OnLocatedEventDragged( void CaptureModeSession::OnLocatedEventDragged(
...@@ -614,10 +737,15 @@ void CaptureModeSession::OnLocatedEventDragged( ...@@ -614,10 +737,15 @@ void CaptureModeSession::OnLocatedEventDragged(
} }
void CaptureModeSession::OnLocatedEventReleased( void CaptureModeSession::OnLocatedEventReleased(
const gfx::Point& location_in_root) { const gfx::Point& location_in_root,
bool is_event_on_capture_bar) {
fine_tune_position_ = FineTunePosition::kNone; fine_tune_position_ = FineTunePosition::kNone;
anchor_points_.clear(); anchor_points_.clear();
cursor_setter_->UpdateCursor(
GetCursorType(GetFineTunePosition(location_in_root, /*is_touch=*/false),
is_event_on_capture_bar));
// Do a repaint to show the affordance circles. See UpdateCaptureRegion to see // Do a repaint to show the affordance circles. See UpdateCaptureRegion to see
// how damage is calculated. // how damage is calculated.
gfx::Rect damage_region = controller_->user_capture_region(); gfx::Rect damage_region = controller_->user_capture_region();
......
...@@ -103,6 +103,8 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -103,6 +103,8 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
} }
private: private:
class ScopedCursorSetter;
// Gets the bounds of current window selected for |kWindow| capture source. // Gets the bounds of current window selected for |kWindow| capture source.
gfx::Rect GetSelectedWindowBounds() const; gfx::Rect GetSelectedWindowBounds() const;
...@@ -121,12 +123,22 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -121,12 +123,22 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// we will use larger hit targets for the drag affordances. // we will use larger hit targets for the drag affordances.
void OnLocatedEvent(ui::LocatedEvent* event, bool is_touch); void OnLocatedEvent(ui::LocatedEvent* event, bool is_touch);
// Returns the fine tune position that corresponds to the given
// |location_in_root|.
FineTunePosition GetFineTunePosition(const gfx::Point& location_in_root,
bool is_touch) const;
// Returns the expected cursor type.
ui::mojom::CursorType GetCursorType(FineTunePosition position,
bool is_event_on_capture_bar) const;
// Handles updating the select region UI. // Handles updating the select region UI.
void OnLocatedEventPressed(const gfx::Point& location_in_root, void OnLocatedEventPressed(const gfx::Point& location_in_root,
bool is_touch, bool is_touch,
bool is_event_on_capture_bar); bool is_event_on_capture_bar);
void OnLocatedEventDragged(const gfx::Point& location_in_root); void OnLocatedEventDragged(const gfx::Point& location_in_root);
void OnLocatedEventReleased(const gfx::Point& location_in_root); void OnLocatedEventReleased(const gfx::Point& location_in_root,
bool is_event_on_capture_bar);
// Updates the capture region and the capture region widgets depending on the // Updates the capture region and the capture region widgets depending on the
// value of |is_resizing|. // value of |is_resizing|.
...@@ -235,6 +247,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -235,6 +247,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// Contains the window dimmers which dim all the root windows except // Contains the window dimmers which dim all the root windows except
// |current_root_|. // |current_root_|.
base::flat_set<std::unique_ptr<WindowDimmer>> root_window_dimmers_; base::flat_set<std::unique_ptr<WindowDimmer>> root_window_dimmers_;
// The object to specify the cursor type.
std::unique_ptr<ScopedCursorSetter> cursor_setter_;
}; };
} // namespace ash } // namespace ash
......
...@@ -835,4 +835,91 @@ TEST_F(CaptureModeTest, MultiDisplayRegionSourceRootWindow) { ...@@ -835,4 +835,91 @@ TEST_F(CaptureModeTest, MultiDisplayRegionSourceRootWindow) {
->GetBoundsInScreen())); ->GetBoundsInScreen()));
} }
TEST_F(CaptureModeTest, RegionCursorStates) {
using ui::mojom::CursorType;
auto* cursor_manager = Shell::Get()->cursor_manager();
CursorType original_cursor_type = cursor_manager->GetCursor().type();
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(CursorType::kPointer, original_cursor_type);
auto* event_generator = GetEventGenerator();
auto* controller = StartImageRegionCapture();
EXPECT_TRUE(cursor_manager->IsCursorLocked());
event_generator->MoveMouseTo(gfx::Point(175, 175));
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kCell, cursor_manager->GetCursor().type());
const gfx::Rect target_region(gfx::Rect(200, 200, 400, 400));
SelectRegion(target_region);
// Makes sure that the cursor is updated when the user releases the region
// select and is still hovering in the same location.
EXPECT_EQ(CursorType::kSouthEastResize, cursor_manager->GetCursor().type());
// Verify that all of the |FineTunePosition| locations have the correct cursor
// when hovered over.
event_generator->MoveMouseTo(target_region.origin());
EXPECT_EQ(CursorType::kNorthWestResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.top_center());
EXPECT_EQ(CursorType::kNorthSouthResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.top_right());
EXPECT_EQ(CursorType::kNorthEastResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.right_center());
EXPECT_EQ(CursorType::kEastWestResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.bottom_right());
EXPECT_EQ(CursorType::kSouthEastResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.bottom_center());
EXPECT_EQ(CursorType::kNorthSouthResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.bottom_left());
EXPECT_EQ(CursorType::kSouthWestResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.left_center());
EXPECT_EQ(CursorType::kEastWestResize, cursor_manager->GetCursor().type());
// Tests that within the bounds of the selected region, the cursor is a hand
// when hovering over the capture button, otherwise it is a multi-directional
// move cursor.
event_generator->MoveMouseTo(gfx::Point(250, 250));
EXPECT_EQ(CursorType::kMove, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(target_region.CenterPoint());
EXPECT_EQ(CursorType::kHand, cursor_manager->GetCursor().type());
// Tests that the cursor changes to a cell type when hovering over the
// unselected region.
event_generator->MoveMouseTo(gfx::Point(50, 50));
EXPECT_EQ(CursorType::kCell, cursor_manager->GetCursor().type());
// Check that cursor is unlocked when changing sources, and that the cursor
// changes to a pointer when hovering over the capture mode bar.
event_generator->MoveMouseTo(
GetRegionToggleButton()->GetBoundsInScreen().CenterPoint());
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(
GetWindowToggleButton()->GetBoundsInScreen().CenterPoint());
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
event_generator->ClickLeftButton();
ASSERT_EQ(CaptureModeSource::kWindow, controller->source());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
// Tests that on changing back to region capture mode, the cursor becomes
// locked, and is still a pointer type over the bar, whilst a cell cursor
// otherwise (not over the selected region).
event_generator->MoveMouseTo(
GetRegionToggleButton()->GetBoundsInScreen().CenterPoint());
original_cursor_type = cursor_manager->GetCursor().type();
event_generator->ClickLeftButton();
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(gfx::Point(50, 50));
EXPECT_EQ(CursorType::kCell, cursor_manager->GetCursor().type());
// Tests that when exiting capture mode that the cursor is restored to its
// original state.
controller->Stop();
EXPECT_FALSE(controller->IsActive());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
}
} // namespace ash } // namespace ash
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