Commit 5fdbd748 authored by Sean O'Brien's avatar Sean O'Brien Committed by Commit Bot

Accelerate long press gesture with stylus button

ChromeOS devices currently do not have styluses with buttons,
and there is no support for handling stylus button events.

In the long term, we will want to make stylus button clicks act
as a right mouse click button (open context menu).  In the short
term, we want to use the stylus button to accelerate the long press
gesture.

If the user has the stylus button pressed when touching the screen,
or presses it before the long press timeout, generate a long press
gesture.

Bug: b:139781900
Change-Id: I3869989f3f731de18ecec480577eb209c08b9c6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2076663Reviewed-by: default avatarNavid Zolghadr <nzolghadr@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Commit-Queue: Sean O'Brien <seobrien@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749240}
parent 9b9dd55c
...@@ -311,6 +311,9 @@ class PaletteTrayTestWithAssistant : public PaletteTrayTest { ...@@ -311,6 +311,9 @@ class PaletteTrayTestWithAssistant : public PaletteTrayTest {
EXPECT_EQ(expected, highlighter_showing()); EXPECT_EQ(expected, highlighter_showing());
EXPECT_EQ(expected, metalayer_enabled()); EXPECT_EQ(expected, metalayer_enabled());
generator->ReleaseTouch(); generator->ReleaseTouch();
// If the tool is not enabled, the gesture may open a context menu instead.
// Press escape to close the menu.
generator->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
} }
void WaitDragAndAssertMetalayer(const std::string& context, void WaitDragAndAssertMetalayer(const std::string& context,
......
...@@ -150,6 +150,19 @@ void MetalayerMode::OnTouchEvent(ui::TouchEvent* event) { ...@@ -150,6 +150,19 @@ void MetalayerMode::OnTouchEvent(ui::TouchEvent* event) {
event->StopPropagation(); event->StopPropagation();
} }
void MetalayerMode::OnGestureEvent(ui::GestureEvent* event) {
if (!feature_enabled())
return;
// When the stylus button is pressed, a ET_GESTURE_LONG_PRESS event with
// EF_LEFT_MOUSE_BUTTON will be generated by the GestureDetector. If the
// metalayer feature is enabled, these should be consumed.
if (event->type() == ui::ET_GESTURE_LONG_PRESS &&
(event->flags() & ui::EF_LEFT_MOUSE_BUTTON)) {
event->StopPropagation();
}
}
void MetalayerMode::OnAssistantStatusChanged(mojom::AssistantState state) { void MetalayerMode::OnAssistantStatusChanged(mojom::AssistantState state) {
assistant_state_ = state; assistant_state_ = state;
UpdateState(); UpdateState();
......
...@@ -62,6 +62,7 @@ class ASH_EXPORT MetalayerMode : public CommonPaletteTool, ...@@ -62,6 +62,7 @@ class ASH_EXPORT MetalayerMode : public CommonPaletteTool,
// ui::EventHandler: // ui::EventHandler:
void OnTouchEvent(ui::TouchEvent* event) override; void OnTouchEvent(ui::TouchEvent* event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
// AssistantStateObserver: // AssistantStateObserver:
void OnAssistantStatusChanged(mojom::AssistantState state) override; void OnAssistantStatusChanged(mojom::AssistantState state) override;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/numerics/ranges.h" #include "base/numerics/ranges.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/gesture_listeners.h" #include "ui/events/gesture_detection/gesture_listeners.h"
#include "ui/events/gesture_detection/motion_event.h" #include "ui/events/gesture_detection/motion_event.h"
#include "ui/gfx/geometry/angle_conversions.h" #include "ui/gfx/geometry/angle_conversions.h"
...@@ -51,6 +52,11 @@ GestureDetector::Config::Config() ...@@ -51,6 +52,11 @@ GestureDetector::Config::Config()
two_finger_tap_max_separation(300), two_finger_tap_max_separation(300),
two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)), two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)),
single_tap_repeat_interval(1), single_tap_repeat_interval(1),
#if defined(OS_CHROMEOS)
stylus_button_accelerated_longpress_enabled(true),
#else
stylus_button_accelerated_longpress_enabled(false),
#endif
velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) { velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) {
} }
...@@ -135,6 +141,7 @@ GestureDetector::GestureDetector( ...@@ -135,6 +141,7 @@ GestureDetector::GestureDetector(
last_focus_y_(0), last_focus_y_(0),
down_focus_x_(0), down_focus_x_(0),
down_focus_y_(0), down_focus_y_(0),
stylus_button_accelerated_longpress_enabled_(false),
longpress_enabled_(true), longpress_enabled_(true),
showpress_enabled_(true), showpress_enabled_(true),
swipe_enabled_(false), swipe_enabled_(false),
...@@ -333,6 +340,11 @@ bool GestureDetector::OnTouchEvent(const MotionEvent& ev, ...@@ -333,6 +340,11 @@ bool GestureDetector::OnTouchEvent(const MotionEvent& ev,
last_focus_y_ = focus_y; last_focus_y_ = focus_y;
} }
if (stylus_button_accelerated_longpress_enabled_ &&
(ev.GetFlags() & ui::EF_LEFT_MOUSE_BUTTON)) {
OnStylusButtonPress(ev);
}
if (!two_finger_tap_allowed_for_gesture_) if (!two_finger_tap_allowed_for_gesture_)
break; break;
...@@ -458,6 +470,8 @@ void GestureDetector::Init(const Config& config) { ...@@ -458,6 +470,8 @@ void GestureDetector::Init(const Config& config) {
DCHECK_GE(config.single_tap_repeat_interval, 1); DCHECK_GE(config.single_tap_repeat_interval, 1);
single_tap_repeat_interval_ = config.single_tap_repeat_interval; single_tap_repeat_interval_ = config.single_tap_repeat_interval;
stylus_button_accelerated_longpress_enabled_ =
config.stylus_button_accelerated_longpress_enabled;
} }
void GestureDetector::OnShowPressTimeout() { void GestureDetector::OnShowPressTimeout() {
...@@ -481,6 +495,20 @@ void GestureDetector::OnTapTimeout() { ...@@ -481,6 +495,20 @@ void GestureDetector::OnTapTimeout() {
} }
} }
void GestureDetector::OnStylusButtonPress(const MotionEvent& ev) {
if (!timeout_handler_->HasTimeout(LONG_PRESS))
return;
timeout_handler_->StopTimeout(TAP);
timeout_handler_->StopTimeout(SHOW_PRESS);
timeout_handler_->StopTimeout(LONG_PRESS);
defer_confirm_single_tap_ = false;
// This will generate a ET_GESTURE_LONG_PRESS event with EF_LEFT_MOUSE_BUTTON,
// which is consumed by MetalayerMode if that feature is enabled, because
// MetalayerMode is also activated by a stylus button press and has precedence
// over this press acceleration feature.
listener_->OnLongPress(ev);
}
void GestureDetector::Cancel() { void GestureDetector::Cancel() {
// Stop waiting for a second tap and send a GESTURE_TAP_CANCEL to keep the // Stop waiting for a second tap and send a GESTURE_TAP_CANCEL to keep the
// gesture stream valid. // gesture stream valid.
......
...@@ -77,6 +77,10 @@ class GESTURE_DETECTION_EXPORT GestureDetector { ...@@ -77,6 +77,10 @@ class GESTURE_DETECTION_EXPORT GestureDetector {
// count will always be 1. // count will always be 1.
int single_tap_repeat_interval; int single_tap_repeat_interval;
// Whether a longpress should be generated immediately when a stylus button
// is pressed, given that the longpress timeout is still active.
bool stylus_button_accelerated_longpress_enabled;
VelocityTracker::Strategy velocity_tracker_strategy; VelocityTracker::Strategy velocity_tracker_strategy;
}; };
...@@ -112,6 +116,7 @@ class GESTURE_DETECTION_EXPORT GestureDetector { ...@@ -112,6 +116,7 @@ class GESTURE_DETECTION_EXPORT GestureDetector {
void OnShowPressTimeout(); void OnShowPressTimeout();
void OnLongPressTimeout(); void OnLongPressTimeout();
void OnTapTimeout(); void OnTapTimeout();
void OnStylusButtonPress(const MotionEvent& ev);
void Cancel(); void Cancel();
void CancelTaps(); void CancelTaps();
bool IsRepeatedTap(const MotionEvent& first_down, bool IsRepeatedTap(const MotionEvent& first_down,
...@@ -172,6 +177,7 @@ class GESTURE_DETECTION_EXPORT GestureDetector { ...@@ -172,6 +177,7 @@ class GESTURE_DETECTION_EXPORT GestureDetector {
float down_focus_x_; float down_focus_x_;
float down_focus_y_; float down_focus_y_;
bool stylus_button_accelerated_longpress_enabled_;
bool longpress_enabled_; bool longpress_enabled_;
bool showpress_enabled_; bool showpress_enabled_;
bool swipe_enabled_; bool swipe_enabled_;
......
...@@ -298,6 +298,13 @@ class GestureProviderTest : public testing::Test, public GestureProviderClient { ...@@ -298,6 +298,13 @@ class GestureProviderTest : public testing::Test, public GestureProviderClient {
SetUpWithConfig(config); SetUpWithConfig(config);
} }
void SetStylusButtonAcceleratedLongPress(bool enabled) {
GestureProvider::Config config = GetDefaultConfig();
config.gesture_detector_config.stylus_button_accelerated_longpress_enabled =
enabled;
SetUpWithConfig(config);
}
bool HasDownEvent() const { return gesture_provider_->current_down_event(); } bool HasDownEvent() const { return gesture_provider_->current_down_event(); }
protected: protected:
...@@ -1249,6 +1256,36 @@ TEST_F(GestureProviderTest, GestureLongPressDoesNotPreventScrolling) { ...@@ -1249,6 +1256,36 @@ TEST_F(GestureProviderTest, GestureLongPressDoesNotPreventScrolling) {
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP)); EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
} }
TEST_F(GestureProviderTest, StylusButtonCausesLongPress) {
SetStylusButtonAcceleratedLongPress(true);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::Action::MOVE);
event.set_flags(EF_LEFT_MOUSE_BUTTON);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
}
TEST_F(GestureProviderTest, DisabledStylusButtonDoesNotCauseLongPress) {
SetStylusButtonAcceleratedLongPress(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::Action::MOVE);
event.set_flags(EF_LEFT_MOUSE_BUTTON);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_NE(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
}
TEST_F(GestureProviderTest, NoGestureLongPressDuringDoubleTap) { TEST_F(GestureProviderTest, NoGestureLongPressDuringDoubleTap) {
base::TimeTicks event_time = base::TimeTicks::Now(); base::TimeTicks event_time = base::TimeTicks::Now();
int motion_event_id = 6; int motion_event_id = 6;
......
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