1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include <limits>
#include "ui/gfx/geometry/transform.h"
namespace blink {
namespace {
bool IsContinuousGestureEvent(WebInputEvent::Type type) {
switch (type) {
case WebGestureEvent::Type::kGestureScrollUpdate:
case WebGestureEvent::Type::kGesturePinchUpdate:
return true;
default:
return false;
}
}
// Returns the transform matrix corresponding to the gesture event.
gfx::Transform GetTransformForEvent(const WebGestureEvent& gesture_event) {
gfx::Transform gesture_transform;
if (gesture_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
gesture_transform.Translate(gesture_event.data.scroll_update.delta_x,
gesture_event.data.scroll_update.delta_y);
} else if (gesture_event.GetType() ==
WebInputEvent::Type::kGesturePinchUpdate) {
float scale = gesture_event.data.pinch_update.scale;
gesture_transform.Translate(-gesture_event.PositionInWidget().x(),
-gesture_event.PositionInWidget().y());
gesture_transform.Scale(scale, scale);
gesture_transform.Translate(gesture_event.PositionInWidget().x(),
gesture_event.PositionInWidget().y());
} else {
NOTREACHED() << "Invalid event type for transform retrieval: "
<< WebInputEvent::GetName(gesture_event.GetType());
}
return gesture_transform;
}
} // namespace
std::unique_ptr<WebInputEvent> WebGestureEvent::Clone() const {
return std::make_unique<WebGestureEvent>(*this);
}
bool WebGestureEvent::CanCoalesce(const WebInputEvent& event) const {
if (!IsGestureEventType(event.GetType()))
return false;
const WebGestureEvent& gesture_event =
static_cast<const WebGestureEvent&>(event);
if (GetType() != gesture_event.GetType() ||
SourceDevice() != gesture_event.SourceDevice() ||
GetModifiers() != gesture_event.GetModifiers())
return false;
if (GetType() == WebInputEvent::Type::kGestureScrollUpdate)
return true;
// GesturePinchUpdate scales can be combined only if they share a focal point,
// e.g., with double-tap drag zoom.
// Due to the imprecision of OOPIF coordinate conversions, the positions may
// not be exactly equal, so we only require approximate equality.
constexpr float kAnchorTolerance = 1.f;
if (GetType() == WebInputEvent::Type::kGesturePinchUpdate &&
(std::abs(PositionInWidget().x() - gesture_event.PositionInWidget().x()) <
kAnchorTolerance) &&
(std::abs(PositionInWidget().y() - gesture_event.PositionInWidget().y()) <
kAnchorTolerance)) {
return true;
}
return false;
}
void WebGestureEvent::Coalesce(const WebInputEvent& event) {
DCHECK(CanCoalesce(event));
const WebGestureEvent& gesture_event =
static_cast<const WebGestureEvent&>(event);
if (GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
data.scroll_update.delta_x += gesture_event.data.scroll_update.delta_x;
data.scroll_update.delta_y += gesture_event.data.scroll_update.delta_y;
} else if (GetType() == WebInputEvent::Type::kGesturePinchUpdate) {
data.pinch_update.scale *= gesture_event.data.pinch_update.scale;
// Ensure the scale remains bounded above 0 and below Infinity so that
// we can reliably perform operations like log on the values.
if (data.pinch_update.scale < std::numeric_limits<float>::min())
data.pinch_update.scale = std::numeric_limits<float>::min();
else if (data.pinch_update.scale > std::numeric_limits<float>::max())
data.pinch_update.scale = std::numeric_limits<float>::max();
}
}
ui::ScrollInputType WebGestureEvent::GetScrollInputType() const {
switch (SourceDevice()) {
case WebGestureDevice::kTouchpad:
DCHECK(IsGestureScroll() || IsPinchGestureEventType(GetType()));
// TODO(crbug.com/1060268): Use of Wheel for Touchpad, especially for
// pinch events, is confusing and not ideal. There are currently a few
// different enum types in use across chromium code base for specifying
// gesture input device. Since we don't want to add yet another one, the
// most appropriate enum type to use here seems to be
// `ui::ScrollInputType` which does not have a separate value for
// touchpad. There is an intention to unify all these enum types. We
// should consider having a separate touchpad device type in the unified
// enum type.
return ui::ScrollInputType::kWheel;
case WebGestureDevice::kTouchscreen:
DCHECK(IsGestureScroll() || IsPinchGestureEventType(GetType()));
return ui::ScrollInputType::kTouchscreen;
case WebGestureDevice::kSyntheticAutoscroll:
DCHECK(IsGestureScroll());
return ui::ScrollInputType::kAutoscroll;
case WebGestureDevice::kScrollbar:
DCHECK(IsGestureScroll());
return ui::ScrollInputType::kScrollbar;
case WebGestureDevice::kUninitialized:
break;
}
NOTREACHED();
}
float WebGestureEvent::DeltaXInRootFrame() const {
const float delta_x = (type_ == WebInputEvent::Type::kGestureScrollBegin)
? data.scroll_begin.delta_x_hint
: data.scroll_update.delta_x;
return delta_x / frame_scale_;
}
float WebGestureEvent::DeltaYInRootFrame() const {
const float delta_y = (type_ == WebInputEvent::Type::kGestureScrollBegin)
? data.scroll_begin.delta_y_hint
: data.scroll_update.delta_y;
return delta_y / frame_scale_;
}
ui::ScrollGranularity WebGestureEvent::DeltaUnits() const {
if (type_ == WebInputEvent::Type::kGestureScrollBegin)
return data.scroll_begin.delta_hint_units;
if (type_ == WebInputEvent::Type::kGestureScrollUpdate)
return data.scroll_update.delta_units;
DCHECK_EQ(type_, WebInputEvent::Type::kGestureScrollEnd);
return data.scroll_end.delta_units;
}
WebGestureEvent::InertialPhaseState WebGestureEvent::InertialPhase() const {
if (type_ == WebInputEvent::Type::kGestureScrollBegin)
return data.scroll_begin.inertial_phase;
if (type_ == WebInputEvent::Type::kGestureScrollUpdate)
return data.scroll_update.inertial_phase;
DCHECK_EQ(type_, WebInputEvent::Type::kGestureScrollEnd);
return data.scroll_end.inertial_phase;
}
bool WebGestureEvent::Synthetic() const {
if (type_ == WebInputEvent::Type::kGestureScrollBegin)
return data.scroll_begin.synthetic;
DCHECK_EQ(type_, WebInputEvent::Type::kGestureScrollEnd);
return data.scroll_end.synthetic;
}
gfx::SizeF WebGestureEvent::TapAreaInRootFrame() const {
if (type_ == WebInputEvent::Type::kGestureTwoFingerTap) {
return gfx::SizeF(data.two_finger_tap.first_finger_width / frame_scale_,
data.two_finger_tap.first_finger_height / frame_scale_);
} else if (type_ == WebInputEvent::Type::kGestureShortPress ||
type_ == WebInputEvent::Type::kGestureLongPress ||
type_ == WebInputEvent::Type::kGestureLongTap) {
return gfx::SizeF(data.long_press.width / frame_scale_,
data.long_press.height / frame_scale_);
} else if (type_ == WebInputEvent::Type::kGestureTap ||
type_ == WebInputEvent::Type::kGestureTapUnconfirmed ||
type_ == WebInputEvent::Type::kGestureDoubleTap) {
return gfx::SizeF(data.tap.width / frame_scale_,
data.tap.height / frame_scale_);
} else if (type_ == WebInputEvent::Type::kGestureTapDown) {
return gfx::SizeF(data.tap_down.width / frame_scale_,
data.tap_down.height / frame_scale_);
} else if (type_ == WebInputEvent::Type::kGestureShowPress) {
return gfx::SizeF(data.show_press.width / frame_scale_,
data.show_press.height / frame_scale_);
}
// This function is called for all gestures and determined if the tap
// area is empty or not; so return an empty rect here.
return gfx::SizeF();
}
gfx::PointF WebGestureEvent::PositionInRootFrame() const {
return gfx::ScalePoint(position_in_widget_, 1 / frame_scale_) +
frame_translate_;
}
int WebGestureEvent::TapCount() const {
DCHECK_EQ(type_, WebInputEvent::Type::kGestureTap);
return data.tap.tap_count;
}
int WebGestureEvent::TapDownCount() const {
DCHECK_EQ(type_, WebInputEvent::Type::kGestureTapDown);
return data.tap_down.tap_down_count;
}
void WebGestureEvent::ApplyTouchAdjustment(
const gfx::PointF& root_frame_coords) {
// Update the window-relative position of the event so that the node that
// was ultimately hit is under this point (i.e. elementFromPoint for the
// client co-ordinates in a 'click' event should yield the target). The
// global position is intentionally left unmodified because it's intended to
// reflect raw co-ordinates unrelated to any content.
frame_translate_ = root_frame_coords -
gfx::ScalePoint(position_in_widget_, 1 / frame_scale_);
}
void WebGestureEvent::FlattenTransform() {
if (frame_scale_ != 1) {
switch (type_) {
case WebInputEvent::Type::kGestureScrollBegin:
data.scroll_begin.delta_x_hint /= frame_scale_;
data.scroll_begin.delta_y_hint /= frame_scale_;
break;
case WebInputEvent::Type::kGestureScrollUpdate:
data.scroll_update.delta_x /= frame_scale_;
data.scroll_update.delta_y /= frame_scale_;
break;
case WebInputEvent::Type::kGestureTwoFingerTap:
data.two_finger_tap.first_finger_width /= frame_scale_;
data.two_finger_tap.first_finger_height /= frame_scale_;
break;
case WebInputEvent::Type::kGestureShortPress:
case WebInputEvent::Type::kGestureLongPress:
case WebInputEvent::Type::kGestureLongTap:
data.long_press.width /= frame_scale_;
data.long_press.height /= frame_scale_;
break;
case WebInputEvent::Type::kGestureTap:
case WebInputEvent::Type::kGestureTapUnconfirmed:
case WebInputEvent::Type::kGestureDoubleTap:
data.tap.width /= frame_scale_;
data.tap.height /= frame_scale_;
break;
case WebInputEvent::Type::kGestureTapDown:
data.tap_down.width /= frame_scale_;
data.tap_down.height /= frame_scale_;
break;
case WebInputEvent::Type::kGestureShowPress:
data.show_press.width /= frame_scale_;
data.show_press.height /= frame_scale_;
break;
default:
break;
}
}
SetPositionInWidget(PositionInRootFrame());
frame_translate_ = gfx::Vector2dF();
frame_scale_ = 1;
}
// Whether |event_in_queue| is a touchscreen GesturePinchUpdate or
// GestureScrollUpdate and has the same modifiers/source as the new
// scroll/pinch event. Compatible touchscreen scroll and pinch event pairs
// can be logically coalesced.
bool WebGestureEvent::IsCompatibleScrollorPinch(
const WebGestureEvent& new_event,
const WebGestureEvent& event_in_queue) {
DCHECK(new_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate ||
new_event.GetType() == WebInputEvent::Type::kGesturePinchUpdate)
<< "Invalid event type for pinch/scroll coalescing: "
<< WebInputEvent::GetName(new_event.GetType());
DLOG_IF(WARNING, new_event.TimeStamp() < event_in_queue.TimeStamp())
<< "Event time not monotonic?\n";
return (event_in_queue.GetType() ==
WebInputEvent::Type::kGestureScrollUpdate ||
event_in_queue.GetType() ==
WebInputEvent::Type::kGesturePinchUpdate) &&
event_in_queue.GetModifiers() == new_event.GetModifiers() &&
event_in_queue.SourceDevice() == WebGestureDevice::kTouchscreen &&
new_event.SourceDevice() == WebGestureDevice::kTouchscreen;
}
std::pair<std::unique_ptr<WebGestureEvent>, std::unique_ptr<WebGestureEvent>>
WebGestureEvent::CoalesceScrollAndPinch(
const WebGestureEvent* second_last_event,
const WebGestureEvent& last_event,
const WebGestureEvent& new_event) {
DCHECK(!last_event.CanCoalesce(new_event))
<< "New event can't be coalesced with the last event in queue directly.";
DCHECK(IsContinuousGestureEvent(new_event.GetType()));
DCHECK(IsCompatibleScrollorPinch(new_event, last_event));
DCHECK(!second_last_event ||
IsCompatibleScrollorPinch(new_event, *second_last_event));
auto scroll_event = std::make_unique<WebGestureEvent>(
WebInputEvent::Type::kGestureScrollUpdate, new_event.GetModifiers(),
new_event.TimeStamp(), new_event.SourceDevice());
scroll_event->primary_pointer_type = new_event.primary_pointer_type;
scroll_event->primary_unique_touch_event_id =
new_event.primary_unique_touch_event_id;
auto pinch_event = std::make_unique<WebGestureEvent>(*scroll_event);
pinch_event->SetType(WebInputEvent::Type::kGesturePinchUpdate);
pinch_event->SetPositionInWidget(
new_event.GetType() == WebInputEvent::Type::kGesturePinchUpdate
? new_event.PositionInWidget()
: last_event.PositionInWidget());
gfx::Transform combined_scroll_pinch = GetTransformForEvent(last_event);
if (second_last_event) {
combined_scroll_pinch.PreConcat(GetTransformForEvent(*second_last_event));
}
combined_scroll_pinch.PostConcat(GetTransformForEvent(new_event));
float combined_scale = combined_scroll_pinch.To2dScale().x();
gfx::Vector2dF combined_translation = combined_scroll_pinch.To2dTranslation();
scroll_event->data.scroll_update.delta_x =
(combined_translation.x() + pinch_event->PositionInWidget().x()) /
combined_scale -
pinch_event->PositionInWidget().x();
scroll_event->data.scroll_update.delta_y =
(combined_translation.y() + pinch_event->PositionInWidget().y()) /
combined_scale -
pinch_event->PositionInWidget().y();
pinch_event->data.pinch_update.scale = combined_scale;
return std::make_pair(std::move(scroll_event), std::move(pinch_event));
}
std::unique_ptr<blink::WebGestureEvent>
WebGestureEvent::GenerateInjectedScrollbarGestureScroll(
WebInputEvent::Type type,
base::TimeTicks timestamp,
gfx::PointF position_in_widget,
gfx::Vector2dF scroll_delta,
ui::ScrollGranularity granularity) {
std::unique_ptr<WebGestureEvent> generated_gesture_event =
std::make_unique<WebGestureEvent>(type, WebInputEvent::kNoModifiers,
timestamp,
WebGestureDevice::kScrollbar);
DCHECK(generated_gesture_event->IsGestureScroll());
if (type == WebInputEvent::Type::kGestureScrollBegin) {
// Gesture events expect the scroll delta to be flipped. Gesture events'
// scroll deltas are interpreted as the finger's delta in relation to the
// screen (which is the reverse of the scrolling direction).
generated_gesture_event->data.scroll_begin.delta_x_hint = -scroll_delta.x();
generated_gesture_event->data.scroll_begin.delta_y_hint = -scroll_delta.y();
generated_gesture_event->data.scroll_begin.inertial_phase =
WebGestureEvent::InertialPhaseState::kNonMomentum;
generated_gesture_event->data.scroll_begin.delta_hint_units = granularity;
} else if (type == WebInputEvent::Type::kGestureScrollUpdate) {
generated_gesture_event->data.scroll_update.delta_x = -scroll_delta.x();
generated_gesture_event->data.scroll_update.delta_y = -scroll_delta.y();
generated_gesture_event->data.scroll_update.inertial_phase =
WebGestureEvent::InertialPhaseState::kNonMomentum;
generated_gesture_event->data.scroll_update.delta_units = granularity;
}
generated_gesture_event->SetPositionInWidget(position_in_widget);
return generated_gesture_event;
}
} // namespace blink
|