зеркало из https://github.com/mozilla/gecko-dev.git
Bug 750974: Move basic pan/zoom logic into Gecko C++ r=cjones,roc sr=smaug[widget/]
This commit is contained in:
Родитель
f8077d5147
Коммит
911626890a
|
@ -110,9 +110,13 @@ endif
|
|||
EXPORTS_NAMESPACES = gfxipc mozilla/layers
|
||||
EXPORTS_gfxipc = ShadowLayerUtils.h
|
||||
EXPORTS_mozilla/layers =\
|
||||
AsyncPanZoomController.h \
|
||||
Axis.h \
|
||||
CompositorCocoaWidgetHelper.h \
|
||||
CompositorChild.h \
|
||||
CompositorParent.h \
|
||||
GeckoContentController.h \
|
||||
GestureEventListener.h \
|
||||
ImageBridgeChild.h \
|
||||
ImageBridgeParent.h \
|
||||
ImageContainerChild.h \
|
||||
|
@ -126,9 +130,12 @@ EXPORTS_mozilla/layers =\
|
|||
$(NULL)
|
||||
|
||||
CPPSRCS += \
|
||||
AsyncPanZoomController.cpp \
|
||||
Axis.cpp \
|
||||
CompositorCocoaWidgetHelper.cpp \
|
||||
CompositorChild.cpp \
|
||||
CompositorParent.cpp \
|
||||
GestureEventListener.cpp \
|
||||
ImageBridgeChild.cpp \
|
||||
ImageBridgeParent.cpp \
|
||||
ImageContainerChild.cpp \
|
||||
|
@ -162,4 +169,8 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/content/events/src \
|
||||
$(NULL)
|
||||
|
||||
CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_PIXMAN_CFLAGS) $(TK_CFLAGS)
|
||||
|
|
|
@ -0,0 +1,729 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CompositorParent.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include "mozilla/XPCOM.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "AsyncPanZoomController.h"
|
||||
#include "GestureEventListener.h"
|
||||
#include "nsIThreadManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
static const float EPSILON = 0.0001;
|
||||
|
||||
/**
|
||||
* Maximum amount of time while panning before sending a viewport change. This
|
||||
* will asynchronously repaint the page. It is also forced when panning stops.
|
||||
*/
|
||||
static const PRInt32 PAN_REPAINT_INTERVAL = 250;
|
||||
|
||||
/**
|
||||
* Maximum amount of time flinging before sending a viewport change. This will
|
||||
* asynchronously repaint the page.
|
||||
*/
|
||||
static const PRInt32 FLING_REPAINT_INTERVAL = 75;
|
||||
|
||||
AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController,
|
||||
GestureBehavior aGestures)
|
||||
: mGeckoContentController(aGeckoContentController),
|
||||
mX(this),
|
||||
mY(this),
|
||||
mMonitor("AsyncPanZoomController"),
|
||||
mLastSampleTime(TimeStamp::Now()),
|
||||
mState(NOTHING),
|
||||
mDPI(72)
|
||||
{
|
||||
if (aGestures == USE_GESTURE_DETECTOR) {
|
||||
mGestureEventListener = new GestureEventListener(this);
|
||||
}
|
||||
|
||||
SetDPI(mDPI);
|
||||
}
|
||||
|
||||
AsyncPanZoomController::~AsyncPanZoomController() {
|
||||
|
||||
}
|
||||
|
||||
static gfx::Point
|
||||
WidgetSpaceToCompensatedViewportSpace(const gfx::Point& aPoint,
|
||||
gfxFloat aCurrentZoom)
|
||||
{
|
||||
// Transform the input point from local widget space to the content document
|
||||
// space that the user is seeing, from last composite.
|
||||
gfx::Point pt(aPoint);
|
||||
pt = pt / aCurrentZoom;
|
||||
|
||||
// FIXME/bug 775451: this doesn't attempt to compensate for content transforms
|
||||
// in effect on the compositor. The problem is that it's very hard for us to
|
||||
// know what content CSS pixel is at widget point 0,0 based on information
|
||||
// available here. So we use this hacky implementation for now, which works
|
||||
// in quiescent states.
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
AsyncPanZoomController::HandleInputEvent(const nsInputEvent& aEvent,
|
||||
nsInputEvent* aOutEvent)
|
||||
{
|
||||
float currentZoom;
|
||||
gfx::Point currentScrollOffset, lastScrollOffset;
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
currentZoom = mFrameMetrics.mResolution.width;
|
||||
currentScrollOffset = gfx::Point(mFrameMetrics.mViewportScrollOffset.x,
|
||||
mFrameMetrics.mViewportScrollOffset.y);
|
||||
lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mViewportScrollOffset.x,
|
||||
mLastContentPaintMetrics.mViewportScrollOffset.y);
|
||||
}
|
||||
|
||||
nsEventStatus status;
|
||||
switch (aEvent.eventStructType) {
|
||||
case NS_TOUCH_EVENT: {
|
||||
MultiTouchInput event(static_cast<const nsTouchEvent&>(aEvent));
|
||||
status = HandleInputEvent(event);
|
||||
break;
|
||||
}
|
||||
case NS_MOUSE_EVENT: {
|
||||
MultiTouchInput event(static_cast<const nsMouseEvent&>(aEvent));
|
||||
status = HandleInputEvent(event);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
status = nsEventStatus_eIgnore;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (aEvent.eventStructType) {
|
||||
case NS_TOUCH_EVENT: {
|
||||
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aOutEvent);
|
||||
const nsTArray<nsCOMPtr<nsIDOMTouch> >& touches = touchEvent->touches;
|
||||
for (PRUint32 i = 0; i < touches.Length(); ++i) {
|
||||
nsIDOMTouch* touch = touches[i];
|
||||
if (touch) {
|
||||
gfx::Point refPoint = WidgetSpaceToCompensatedViewportSpace(
|
||||
gfx::Point(touch->mRefPoint.x, touch->mRefPoint.y),
|
||||
currentZoom);
|
||||
touch->mRefPoint = nsIntPoint(refPoint.x, refPoint.y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
gfx::Point refPoint = WidgetSpaceToCompensatedViewportSpace(
|
||||
gfx::Point(aOutEvent->refPoint.x, aOutEvent->refPoint.y),
|
||||
currentZoom);
|
||||
aOutEvent->refPoint = nsIntPoint(refPoint.x, refPoint.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
if (mGestureEventListener) {
|
||||
nsEventStatus rv = mGestureEventListener->HandleInputEvent(aEvent);
|
||||
if (rv == nsEventStatus_eConsumeNoDefault)
|
||||
return rv;
|
||||
}
|
||||
|
||||
switch (aEvent.mInputType) {
|
||||
case MULTITOUCH_INPUT: {
|
||||
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
|
||||
switch (multiTouchInput.mType) {
|
||||
case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
|
||||
case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
|
||||
case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
|
||||
case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
|
||||
default: NS_WARNING("Unhandled multitouch"); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PINCHGESTURE_INPUT: {
|
||||
const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
|
||||
switch (pinchGestureInput.mType) {
|
||||
case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
|
||||
case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
|
||||
case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
|
||||
default: NS_WARNING("Unhandled pinch gesture"); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TAPGESTURE_INPUT: {
|
||||
const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
|
||||
switch (tapGestureInput.mType) {
|
||||
case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
|
||||
default: NS_WARNING("Unhandled tap gesture"); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: NS_WARNING("Unhandled input event"); break;
|
||||
}
|
||||
|
||||
mLastEventTime = aEvent.mTime;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
|
||||
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
|
||||
|
||||
nsIntPoint point = touch.mScreenPoint;
|
||||
PRInt32 xPos = point.x, yPos = point.y;
|
||||
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
CancelAnimation();
|
||||
// Fall through.
|
||||
case NOTHING:
|
||||
mX.StartTouch(xPos);
|
||||
mY.StartTouch(yPos);
|
||||
mState = TOUCHING;
|
||||
break;
|
||||
case TOUCHING:
|
||||
case PANNING:
|
||||
case PINCHING:
|
||||
NS_WARNING("Received impossible touch in OnTouchStart");
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Unhandled case in OnTouchStart");
|
||||
break;
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
|
||||
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
|
||||
nsIntPoint point = touch.mScreenPoint;
|
||||
PRInt32 xPos = point.x, yPos = point.y;
|
||||
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
case NOTHING:
|
||||
// May happen if the user double-taps and drags without lifting after the
|
||||
// second tap. Ignore the move if this happens.
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
case TOUCHING: {
|
||||
float panThreshold = 1.0f/16.0f * mDPI;
|
||||
if (PanDistance(aEvent) < panThreshold) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
mLastRepaint = aEvent.mTime;
|
||||
mX.StartTouch(xPos);
|
||||
mY.StartTouch(yPos);
|
||||
mState = PANNING;
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
case PANNING:
|
||||
TrackTouch(aEvent);
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
|
||||
case PINCHING:
|
||||
// The scale gesture listener should have handled this.
|
||||
NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
// Should never happen.
|
||||
NS_WARNING("Received impossible touch end in OnTouchEnd.");
|
||||
// Fall through.
|
||||
case NOTHING:
|
||||
// May happen if the user double-taps and drags without lifting after the
|
||||
// second tap. Ignore if this happens.
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
case TOUCHING:
|
||||
mState = NOTHING;
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
case PANNING:
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
ScheduleComposite();
|
||||
RequestContentRepaint();
|
||||
}
|
||||
mState = FLING;
|
||||
mLastSampleTime = TimeStamp::Now();
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
case PINCHING:
|
||||
mState = NOTHING;
|
||||
// Scale gesture listener should have handled this.
|
||||
NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
|
||||
mState = NOTHING;
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
|
||||
mState = PINCHING;
|
||||
mLastZoomFocus = aEvent.mFocusPoint;
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
|
||||
float prevSpan = aEvent.mPreviousSpan;
|
||||
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
|
||||
// We're still handling it; we've just decided to throw this event away.
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
|
||||
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
|
||||
float scale = mFrameMetrics.mResolution.width;
|
||||
|
||||
nsIntPoint focusPoint = aEvent.mFocusPoint;
|
||||
PRInt32 xFocusChange = (mLastZoomFocus.x - focusPoint.x) / scale, yFocusChange = (mLastZoomFocus.y - focusPoint.y) / scale;
|
||||
// If displacing by the change in focus point will take us off page bounds,
|
||||
// then reduce the displacement such that it doesn't.
|
||||
if (mX.DisplacementWillOverscroll(xFocusChange) != Axis::OVERSCROLL_NONE) {
|
||||
xFocusChange -= mX.DisplacementWillOverscrollAmount(xFocusChange);
|
||||
}
|
||||
if (mY.DisplacementWillOverscroll(yFocusChange) != Axis::OVERSCROLL_NONE) {
|
||||
yFocusChange -= mY.DisplacementWillOverscrollAmount(yFocusChange);
|
||||
}
|
||||
ScrollBy(nsIntPoint(xFocusChange, yFocusChange));
|
||||
|
||||
// When we zoom in with focus, we can zoom too much towards the boundaries
|
||||
// that we actually go over them. These are the needed displacements along
|
||||
// either axis such that we don't overscroll the boundaries when zooming.
|
||||
PRInt32 neededDisplacementX = 0, neededDisplacementY = 0;
|
||||
|
||||
// Only do the scaling if we won't go over 8x zoom in or out.
|
||||
bool doScale = (scale < 8.0f && spanRatio > 1.0f) || (scale > 0.125f && spanRatio < 1.0f);
|
||||
|
||||
// If this zoom will take it over 8x zoom in either direction, but it's not
|
||||
// already there, then normalize it.
|
||||
if (scale * spanRatio > 8.0f) {
|
||||
spanRatio = scale / 8.0f;
|
||||
} else if (scale * spanRatio < 0.125f) {
|
||||
spanRatio = scale / 0.125f;
|
||||
}
|
||||
|
||||
if (doScale) {
|
||||
switch (mX.ScaleWillOverscroll(spanRatio, focusPoint.x))
|
||||
{
|
||||
case Axis::OVERSCROLL_NONE:
|
||||
break;
|
||||
case Axis::OVERSCROLL_MINUS:
|
||||
case Axis::OVERSCROLL_PLUS:
|
||||
neededDisplacementX = -mX.ScaleWillOverscrollAmount(spanRatio, focusPoint.x);
|
||||
break;
|
||||
case Axis::OVERSCROLL_BOTH:
|
||||
// If scaling this way will make us overscroll in both directions, then
|
||||
// we must already be at the maximum zoomed out amount. In this case, we
|
||||
// don't want to allow this scaling to go through and instead clamp it
|
||||
// here.
|
||||
doScale = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (doScale) {
|
||||
switch (mY.ScaleWillOverscroll(spanRatio, focusPoint.y))
|
||||
{
|
||||
case Axis::OVERSCROLL_NONE:
|
||||
break;
|
||||
case Axis::OVERSCROLL_MINUS:
|
||||
case Axis::OVERSCROLL_PLUS:
|
||||
neededDisplacementY = -mY.ScaleWillOverscrollAmount(spanRatio, focusPoint.y);
|
||||
break;
|
||||
case Axis::OVERSCROLL_BOTH:
|
||||
doScale = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (doScale) {
|
||||
ScaleWithFocus(scale * spanRatio,
|
||||
focusPoint);
|
||||
|
||||
if (neededDisplacementX != 0 || neededDisplacementY != 0) {
|
||||
ScrollBy(nsIntPoint(neededDisplacementX, neededDisplacementY));
|
||||
}
|
||||
|
||||
ScheduleComposite();
|
||||
// We don't want to redraw on every scale, so don't use
|
||||
// RequestContentRepaint()
|
||||
}
|
||||
|
||||
mLastZoomFocus = focusPoint;
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
|
||||
mState = PANNING;
|
||||
mX.StartTouch(aEvent.mFocusPoint.x);
|
||||
mY.StartTouch(aEvent.mFocusPoint.y);
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
ScheduleComposite();
|
||||
RequestContentRepaint();
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
|
||||
// XXX: Implement this.
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
|
||||
// XXX: Implement this.
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
|
||||
// XXX: Implement this.
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
|
||||
// XXX: Implement this.
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
|
||||
// XXX: Implement this.
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
float AsyncPanZoomController::PanDistance(const MultiTouchInput& aEvent) {
|
||||
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
|
||||
nsIntPoint point = touch.mScreenPoint;
|
||||
PRInt32 xPos = point.x, yPos = point.y;
|
||||
mX.UpdateWithTouchAtDevicePoint(xPos, 0);
|
||||
mY.UpdateWithTouchAtDevicePoint(yPos, 0);
|
||||
return NS_hypot(mX.PanDistance(), mY.PanDistance()) * mFrameMetrics.mResolution.width;
|
||||
}
|
||||
|
||||
const nsPoint AsyncPanZoomController::GetVelocityVector() {
|
||||
return nsPoint(
|
||||
mX.GetVelocity(),
|
||||
mY.GetVelocity()
|
||||
);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
|
||||
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
|
||||
nsIntPoint point = touch.mScreenPoint;
|
||||
PRInt32 xPos = point.x, yPos = point.y, timeDelta = aEvent.mTime - mLastEventTime;
|
||||
|
||||
// Probably a duplicate event, just throw it away.
|
||||
if (!timeDelta) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
mX.UpdateWithTouchAtDevicePoint(xPos, timeDelta);
|
||||
mY.UpdateWithTouchAtDevicePoint(yPos, timeDelta);
|
||||
|
||||
// We want to inversely scale it because when you're zoomed further in, a
|
||||
// larger swipe should move you a shorter distance.
|
||||
float inverseScale = 1 / mFrameMetrics.mResolution.width;
|
||||
|
||||
PRInt32 xDisplacement = mX.UpdateAndGetDisplacement(inverseScale);
|
||||
PRInt32 yDisplacement = mY.UpdateAndGetDisplacement(inverseScale);
|
||||
if (!xDisplacement && !yDisplacement) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScrollBy(nsIntPoint(xDisplacement, yDisplacement));
|
||||
ScheduleComposite();
|
||||
|
||||
if (aEvent.mTime - mLastRepaint >= PAN_REPAINT_INTERVAL) {
|
||||
RequestContentRepaint();
|
||||
mLastRepaint = aEvent.mTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SingleTouchData& AsyncPanZoomController::GetFirstSingleTouch(const MultiTouchInput& aEvent) {
|
||||
return (SingleTouchData&)aEvent.mTouches[0];
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
|
||||
if (mState != FLING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mX.FlingApplyFrictionOrCancel(aDelta) && !mY.FlingApplyFrictionOrCancel(aDelta)) {
|
||||
RequestContentRepaint();
|
||||
mState = NOTHING;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to inversely scale it because when you're zoomed further in, a
|
||||
// larger swipe should move you a shorter distance.
|
||||
float inverseScale = 1 / mFrameMetrics.mResolution.width;
|
||||
|
||||
ScrollBy(nsIntPoint(
|
||||
mX.UpdateAndGetDisplacement(inverseScale),
|
||||
mY.UpdateAndGetDisplacement(inverseScale)
|
||||
));
|
||||
RequestContentRepaint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::CancelAnimation() {
|
||||
mState = NOTHING;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) {
|
||||
mCompositorParent = aCompositorParent;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ScrollBy(const nsIntPoint& aOffset) {
|
||||
nsIntPoint newOffset(mFrameMetrics.mViewportScrollOffset.x + aOffset.x,
|
||||
mFrameMetrics.mViewportScrollOffset.y + aOffset.y);
|
||||
FrameMetrics metrics(mFrameMetrics);
|
||||
metrics.mViewportScrollOffset = newOffset;
|
||||
mFrameMetrics = metrics;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) {
|
||||
FrameMetrics metrics = mFrameMetrics;
|
||||
gfx::Rect pageSize = aCSSPageRect;
|
||||
float scale = mFrameMetrics.mResolution.width;
|
||||
|
||||
// The page rect is the css page rect scaled by the current zoom.
|
||||
pageSize.ScaleRoundOut(scale);
|
||||
|
||||
// Round the page rect so we don't get any truncation, then get the nsIntRect
|
||||
// from this.
|
||||
metrics.mContentRect = nsIntRect(pageSize.x, pageSize.y, pageSize.width, pageSize.height);
|
||||
metrics.mCSSContentRect = aCSSPageRect;
|
||||
|
||||
mFrameMetrics = metrics;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFocus) {
|
||||
FrameMetrics metrics(mFrameMetrics);
|
||||
|
||||
// Don't set the scale to the inputted value, but rather multiply it in.
|
||||
float scaleFactor = aScale / metrics.mResolution.width;
|
||||
|
||||
metrics.mResolution.width = metrics.mResolution.height = aScale;
|
||||
|
||||
// Force a recalculation of the page rect based on the new zoom and the
|
||||
// current CSS page rect (which is unchanged since it's not affected by zoom).
|
||||
SetPageRect(mFrameMetrics.mCSSContentRect);
|
||||
|
||||
nsIntPoint scrollOffset = metrics.mViewportScrollOffset;
|
||||
|
||||
scrollOffset.x += aFocus.x * (scaleFactor - 1.0f);
|
||||
scrollOffset.y += aFocus.y * (scaleFactor - 1.0f);
|
||||
|
||||
metrics.mViewportScrollOffset = scrollOffset;
|
||||
|
||||
mFrameMetrics = metrics;
|
||||
}
|
||||
|
||||
const nsIntRect AsyncPanZoomController::CalculatePendingDisplayPort() {
|
||||
float scale = mFrameMetrics.mResolution.width;
|
||||
nsIntRect viewport = mFrameMetrics.mViewport;
|
||||
viewport.ScaleRoundIn(1 / scale);
|
||||
|
||||
const float SIZE_MULTIPLIER = 2.0f;
|
||||
nsIntPoint scrollOffset = mFrameMetrics.mViewportScrollOffset;
|
||||
gfx::Rect contentRect = mFrameMetrics.mCSSContentRect;
|
||||
|
||||
// Paint a larger portion of the screen than just what we can see. This makes
|
||||
// it less likely that we'll checkerboard when panning around and Gecko hasn't
|
||||
// repainted yet.
|
||||
float desiredWidth = viewport.width * SIZE_MULTIPLIER,
|
||||
desiredHeight = viewport.height * SIZE_MULTIPLIER;
|
||||
|
||||
// The displayport is relative to the current scroll offset. Here's a little
|
||||
// diagram to make it easier to see:
|
||||
//
|
||||
// - - - -
|
||||
// | |
|
||||
// *************
|
||||
// * | | *
|
||||
// - -*- @------ -*- -
|
||||
// | * |=====| * |
|
||||
// * |=====| *
|
||||
// | * |=====| * |
|
||||
// - -*- ------- -*- -
|
||||
// * | | *
|
||||
// *************
|
||||
// | |
|
||||
// - - - -
|
||||
//
|
||||
// The full --- area with === inside it is the actual viewport rect, the *** area
|
||||
// is the displayport, and the - - - area is an imaginary additional page on all 4
|
||||
// borders of the actual page. Notice that the displayport intersects half-way with
|
||||
// each of the imaginary extra pages. The @ symbol at the top left of the
|
||||
// viewport marks the current scroll offset. From the @ symbol to the far left
|
||||
// and far top, it is clear that this distance is 1/4 of the displayport's
|
||||
// height/width dimension.
|
||||
gfx::Rect displayPort(-desiredWidth / 4, -desiredHeight / 4, desiredWidth, desiredHeight);
|
||||
|
||||
// Check if the desired boundaries go over the CSS page rect along the top or
|
||||
// left. If they do, shift them to the right or down.
|
||||
float oldDisplayPortX = displayPort.x, oldDisplayPortY = displayPort.y;
|
||||
if (displayPort.X() + scrollOffset.x < contentRect.X()) {
|
||||
displayPort.x = contentRect.X() - scrollOffset.x;
|
||||
}
|
||||
if (displayPort.Y() + scrollOffset.y < contentRect.Y()) {
|
||||
displayPort.y = contentRect.Y() - scrollOffset.y;
|
||||
}
|
||||
|
||||
// We don't need to paint the extra area that was going to overlap with the
|
||||
// content rect. Subtract out this extra width or height.
|
||||
displayPort.width -= displayPort.x - oldDisplayPortX;
|
||||
displayPort.height -= displayPort.y - oldDisplayPortY;
|
||||
|
||||
// Check if the desired boundaries go over the CSS page rect along the right
|
||||
// or bottom. If they do, subtract out some height or width such that they
|
||||
// perfectly align with the end of the CSS page rect.
|
||||
if (displayPort.XMost() + scrollOffset.x > contentRect.XMost()) {
|
||||
displayPort.width = NS_MAX(0.0f, contentRect.XMost() - (displayPort.X() + scrollOffset.x));
|
||||
}
|
||||
if (displayPort.YMost() + scrollOffset.y > contentRect.YMost()) {
|
||||
displayPort.height = NS_MAX(0.0f, contentRect.YMost() - (displayPort.Y() + scrollOffset.y));
|
||||
}
|
||||
|
||||
// Round the displayport so we don't get any truncation, then get the nsIntRect
|
||||
// from this.
|
||||
displayPort.Round();
|
||||
return nsIntRect(displayPort.x, displayPort.y, displayPort.width, displayPort.height);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SetDPI(int aDPI) {
|
||||
mDPI = aDPI;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ScheduleComposite() {
|
||||
if (mCompositorParent) {
|
||||
mCompositorParent->ScheduleRenderOnCompositorThread();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::RequestContentRepaint() {
|
||||
mFrameMetrics.mDisplayPort = CalculatePendingDisplayPort();
|
||||
mGeckoContentController->RequestContentRepaint(mFrameMetrics);
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
|
||||
const FrameMetrics& aFrame,
|
||||
const gfx3DMatrix& aCurrentTransform,
|
||||
gfx3DMatrix* aNewTransform) {
|
||||
// The eventual return value of this function. The compositor needs to know
|
||||
// whether or not to advance by a frame as soon as it can. For example, if a
|
||||
// fling is happening, it has to keep compositing so that the animation is
|
||||
// smooth. If an animation frame is requested, it is the compositor's
|
||||
// responsibility to schedule a composite.
|
||||
bool requestAnimationFrame = false;
|
||||
|
||||
// Scales on the root layer, on what's currently painted.
|
||||
float rootScaleX = aCurrentTransform.GetXScale(),
|
||||
rootScaleY = aCurrentTransform.GetYScale();
|
||||
|
||||
nsIntPoint metricsScrollOffset(0, 0);
|
||||
nsIntPoint scrollOffset;
|
||||
float localScaleX, localScaleY;
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
|
||||
// If a fling is currently happening, apply it now. We can pull the updated
|
||||
// metrics afterwards.
|
||||
requestAnimationFrame = requestAnimationFrame || DoFling(aSampleTime - mLastSampleTime);
|
||||
|
||||
// Current local transform; this is not what's painted but rather what PZC has
|
||||
// transformed due to touches like panning or pinching. Eventually, the root
|
||||
// layer transform will become this during runtime, but we must wait for Gecko
|
||||
// to repaint.
|
||||
localScaleX = mFrameMetrics.mResolution.width;
|
||||
localScaleY = mFrameMetrics.mResolution.height;
|
||||
|
||||
if (aFrame.IsScrollable()) {
|
||||
metricsScrollOffset = aFrame.mViewportScrollOffset;
|
||||
}
|
||||
|
||||
scrollOffset = mFrameMetrics.mViewportScrollOffset;
|
||||
}
|
||||
|
||||
nsIntPoint scrollCompensation(
|
||||
(scrollOffset.x / rootScaleX - metricsScrollOffset.x) * localScaleX,
|
||||
(scrollOffset.y / rootScaleY - metricsScrollOffset.y) * localScaleY);
|
||||
|
||||
ViewTransform treeTransform(-scrollCompensation, localScaleX, localScaleY);
|
||||
*aNewTransform = gfx3DMatrix(treeTransform) * aCurrentTransform;
|
||||
|
||||
mLastSampleTime = aSampleTime;
|
||||
|
||||
return requestAnimationFrame;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint) {
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
|
||||
mLastContentPaintMetrics = aViewportFrame;
|
||||
|
||||
if (aIsFirstPaint || mFrameMetrics.IsDefault()) {
|
||||
mX.StopTouch();
|
||||
mY.StopTouch();
|
||||
mFrameMetrics = aViewportFrame;
|
||||
mFrameMetrics.mResolution.width = 1 / mFrameMetrics.mResolution.width;
|
||||
mFrameMetrics.mResolution.height = 1 / mFrameMetrics.mResolution.height;
|
||||
SetPageRect(mFrameMetrics.mCSSContentRect);
|
||||
} else if (!mFrameMetrics.mContentRect.IsEqualEdges(aViewportFrame.mContentRect)) {
|
||||
mFrameMetrics.mCSSContentRect = aViewportFrame.mCSSContentRect;
|
||||
SetPageRect(mFrameMetrics.mCSSContentRect);
|
||||
}
|
||||
}
|
||||
|
||||
const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
return mFrameMetrics;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::UpdateViewportSize(int aWidth, int aHeight) {
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
FrameMetrics metrics = GetFrameMetrics();
|
||||
metrics.mViewport = nsIntRect(0, 0, aWidth, aHeight);
|
||||
mFrameMetrics = metrics;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_layers_AsyncPanZoomController_h
|
||||
#define mozilla_layers_AsyncPanZoomController_h
|
||||
|
||||
#include "GeckoContentController.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "InputData.h"
|
||||
#include "Axis.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class CompositorParent;
|
||||
class GestureEventListener;
|
||||
|
||||
/**
|
||||
* Controller for all panning and zooming logic. Any time a user input is
|
||||
* detected and it must be processed in some way to affect what the user sees,
|
||||
* it goes through here. Listens for any input event from InputData and can
|
||||
* optionally handle nsGUIEvent-derived touch events, but this must be done on
|
||||
* the main thread. Note that this class completely cross-platform.
|
||||
*
|
||||
* Input events originate on the UI thread of the platform that this runs on,
|
||||
* and are then sent to this class. This class processes the event in some way;
|
||||
* for example, a touch move will usually lead to a panning of content (though
|
||||
* of course there are exceptions, such as if content preventDefaults the event,
|
||||
* or if the target frame is not scrollable). The compositor interacts with this
|
||||
* class by locking it and querying it for the current transform matrix based on
|
||||
* the panning and zooming logic that was invoked on the UI thread.
|
||||
*
|
||||
* Currently, each outer DOM window (i.e. a website in a tab, but not any
|
||||
* subframes) has its own AsyncPanZoomController. In the future, to support
|
||||
* asynchronously scrolled subframes, we want to have one AsyncPanZoomController
|
||||
* per frame.
|
||||
*/
|
||||
class AsyncPanZoomController {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController)
|
||||
|
||||
typedef mozilla::MonitorAutoLock MonitorAutoLock;
|
||||
|
||||
public:
|
||||
enum GestureBehavior {
|
||||
// The platform code is responsible for forwarding gesture events here. We
|
||||
// will not attempt to generate gesture events from MultiTouchInputs.
|
||||
DEFAULT_GESTURES,
|
||||
// An instance of GestureEventListener is used to detect gestures. This is
|
||||
// handled completely internally within this class.
|
||||
USE_GESTURE_DETECTOR
|
||||
};
|
||||
|
||||
AsyncPanZoomController(GeckoContentController* aController,
|
||||
GestureBehavior aGestures = DEFAULT_GESTURES);
|
||||
~AsyncPanZoomController();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// These methods must only be called on the controller/UI thread.
|
||||
//
|
||||
|
||||
/**
|
||||
* General handler for incoming input events. Manipulates the frame metrics
|
||||
* basde on what type of input it is. For example, a PinchGestureEvent will
|
||||
* cause scaling.
|
||||
*/
|
||||
|
||||
nsEventStatus HandleInputEvent(const InputData& aEvent);
|
||||
|
||||
/**
|
||||
* Special handler for nsInputEvents. Also sets |aOutEvent| (which is assumed
|
||||
* to be an already-existing instance of an nsInputEvent which may be an
|
||||
* nsTouchEvent) to have its touch points in DOM space. This is so that the
|
||||
* touches can be passed through the DOM and content can handle them.
|
||||
*
|
||||
* NOTE: Be careful of invoking the nsInputEvent variant. This can only be
|
||||
* called on the main thread. See widget/InputData.h for more information on
|
||||
* why we have InputData and nsInputEvent separated.
|
||||
*/
|
||||
nsEventStatus HandleInputEvent(const nsInputEvent& aEvent,
|
||||
nsInputEvent* aOutEvent);
|
||||
|
||||
/**
|
||||
* Updates the viewport size, i.e. the dimensions of the frame (not
|
||||
* necessarily the screen) content will actually be rendered onto in device
|
||||
* pixels for example, a subframe will not take the entire screen, but we
|
||||
* still want to know how big it is in device pixels. Ideally we want to be
|
||||
* using CSS pixels everywhere inside here, but in this case we need to know
|
||||
* how large of a displayport to set so we use these dimensions plus some
|
||||
* extra.
|
||||
*
|
||||
* XXX: Use nsIntRect instead.
|
||||
*/
|
||||
void UpdateViewportSize(int aWidth, int aHeight);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// These methods must only be called on the compositor thread.
|
||||
//
|
||||
|
||||
/**
|
||||
* The compositor calls this when it's about to draw pannable/zoomable content
|
||||
* and is setting up transforms for compositing the layer tree. This is not
|
||||
* idempotent. For example, a fling transform can be applied each time this is
|
||||
* called (though not necessarily). |aSampleTime| is the time that this is
|
||||
* sampled at; this is used for interpolating animations. Calling this sets a
|
||||
* new transform in |aNewTransform| which should be applied directly to the
|
||||
* shadow layer of the frame (do not multiply it in as the code already does
|
||||
* this internally with |aCurrentTransform|.
|
||||
*
|
||||
* Return value indicates whether or not any currently running animation
|
||||
* should continue. That is, if true, the compositor should schedule another
|
||||
* composite.
|
||||
*/
|
||||
bool SampleContentTransformForFrame(const TimeStamp& aSampleTime,
|
||||
const FrameMetrics& aFrame,
|
||||
const gfx3DMatrix& aCurrentTransform,
|
||||
gfx3DMatrix* aNewTransform);
|
||||
|
||||
/**
|
||||
* A shadow layer update has arrived. |aViewportFrame| is the new FrameMetrics
|
||||
* for the top-level frame. |aIsFirstPaint| is a flag passed from the shadow
|
||||
* layers code indicating that the frame metrics being sent with this call are
|
||||
* the initial metrics and the initial paint of the frame has just happened.
|
||||
*/
|
||||
void NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint);
|
||||
|
||||
/**
|
||||
* The platform implementation must set the compositor parent so that we can
|
||||
* request composites.
|
||||
*/
|
||||
void SetCompositorParent(CompositorParent* aCompositorParent);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// These methods can be called from any thread.
|
||||
//
|
||||
|
||||
/**
|
||||
* Sets the CSS page rect, and calculates a new page rect based on the zoom
|
||||
* level of the current metrics and the passed in CSS page rect.
|
||||
*/
|
||||
void SetPageRect(const gfx::Rect& aCSSPageRect);
|
||||
|
||||
/**
|
||||
* Sets the DPI of the device for use within panning and zooming logic. It is
|
||||
* a platform responsibility to set this on initialization of this class and
|
||||
* whenever it changes.
|
||||
*/
|
||||
void SetDPI(int aDPI);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Helper method for touches beginning. Sets everything up for panning and any
|
||||
* multitouch gestures.
|
||||
*/
|
||||
nsEventStatus OnTouchStart(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for touches moving. Does any transforms needed when panning.
|
||||
*/
|
||||
nsEventStatus OnTouchMove(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for touches ending. Redraws the screen if necessary and does
|
||||
* any cleanup after a touch has ended.
|
||||
*/
|
||||
nsEventStatus OnTouchEnd(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for touches being cancelled. Treated roughly the same as a
|
||||
* touch ending (OnTouchEnd()).
|
||||
*/
|
||||
nsEventStatus OnTouchCancel(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for scales beginning. Distinct from the OnTouch* handlers in
|
||||
* that this implies some outside implementation has determined that the user
|
||||
* is pinching.
|
||||
*/
|
||||
nsEventStatus OnScaleBegin(const PinchGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for scaling. As the user moves their fingers when pinching,
|
||||
* this changes the scale of the page.
|
||||
*/
|
||||
nsEventStatus OnScale(const PinchGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for scales ending. Redraws the screen if necessary and does
|
||||
* any cleanup after a scale has ended.
|
||||
*/
|
||||
nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for long press gestures.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnLongPress(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for single tap gestures.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for a single tap confirmed.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for double taps.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method to cancel any gesture currently going to Gecko. Used
|
||||
* primarily when a user taps the screen over some clickable content but then
|
||||
* pans down instead of letting go (i.e. to cancel a previous touch so that a
|
||||
* new one can properly take effect.
|
||||
*/
|
||||
nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Scrolls the viewport by an X,Y offset.
|
||||
*/
|
||||
void ScrollBy(const nsIntPoint& aOffset);
|
||||
|
||||
/**
|
||||
* Scales the viewport by an amount (note that it multiplies this scale in to
|
||||
* the current scale, it doesn't set it to |aScale|). Also considers a focus
|
||||
* point so that the page zooms outward from that point.
|
||||
*
|
||||
* XXX: Fix focus point calculations.
|
||||
*/
|
||||
void ScaleWithFocus(float aScale, const nsIntPoint& aFocus);
|
||||
|
||||
/**
|
||||
* Schedules a composite on the compositor thread. Wrapper for
|
||||
* CompositorParent::ScheduleRenderOnCompositorThread().
|
||||
*/
|
||||
void ScheduleComposite();
|
||||
|
||||
/**
|
||||
* Cancels any currently running animation. Note that all this does is set the
|
||||
* state of the AsyncPanZoomController back to NOTHING, but it is the
|
||||
* animation's responsibility to check this before advancing.
|
||||
*/
|
||||
void CancelAnimation();
|
||||
|
||||
/**
|
||||
* Gets the displacement of the current touch since it began. That is, it is
|
||||
* the distance between the current position and the initial position of the
|
||||
* current touch (this only makes sense if a touch is currently happening and
|
||||
* OnTouchMove() is being invoked).
|
||||
*/
|
||||
float PanDistance(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Gets a vector of the velocities of each axis.
|
||||
*/
|
||||
const nsPoint GetVelocityVector();
|
||||
|
||||
/**
|
||||
* Gets a reference to the first SingleTouchData from a MultiTouchInput. This
|
||||
* gets only the first one and assumes the rest are either missing or not
|
||||
* relevant.
|
||||
*/
|
||||
SingleTouchData& GetFirstSingleTouch(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Does any panning required due to a new touch event.
|
||||
*/
|
||||
void TrackTouch(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Recalculates the displayport. Ideally, this should paint an area bigger
|
||||
* than the actual screen. The viewport refers to the size of the screen,
|
||||
* while the displayport is the area actually painted by Gecko. We paint
|
||||
* a larger area than the screen so that when you scroll down, you don't
|
||||
* checkerboard immediately.
|
||||
*/
|
||||
const nsIntRect CalculatePendingDisplayPort();
|
||||
|
||||
/**
|
||||
* Utility function to send updated FrameMetrics to Gecko so that it can paint
|
||||
* the displayport area. Calls into GeckoContentController to do the actual
|
||||
* work.
|
||||
*/
|
||||
void RequestContentRepaint();
|
||||
|
||||
/**
|
||||
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
|
||||
* This should be called whenever sampling the content transform for this
|
||||
* frame. Returns true if the fling animation should be advanced by one frame,
|
||||
* or false if there is no fling or the fling has ended.
|
||||
*/
|
||||
bool DoFling(const TimeDuration& aDelta);
|
||||
|
||||
/**
|
||||
* Gets the current frame metrics. This is *not* the Gecko copy stored in the
|
||||
* layers code.
|
||||
*/
|
||||
const FrameMetrics& GetFrameMetrics();
|
||||
|
||||
private:
|
||||
enum PanZoomState {
|
||||
NOTHING, /* no touch-start events received */
|
||||
FLING, /* all touches removed, but we're still scrolling page */
|
||||
TOUCHING, /* one touch-start event received */
|
||||
PANNING, /* panning without axis lock */
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
};
|
||||
|
||||
nsRefPtr<CompositorParent> mCompositorParent;
|
||||
nsRefPtr<GeckoContentController> mGeckoContentController;
|
||||
nsRefPtr<GestureEventListener> mGestureEventListener;
|
||||
|
||||
// Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
|
||||
// monitor. Do not read from or modify either of them without locking.
|
||||
FrameMetrics mFrameMetrics;
|
||||
// These are the metrics at last content paint, the most recent
|
||||
// values we were notified of in NotifyLayersUpdate().
|
||||
FrameMetrics mLastContentPaintMetrics;
|
||||
|
||||
AxisX mX;
|
||||
AxisY mY;
|
||||
|
||||
Monitor mMonitor;
|
||||
|
||||
// The last time the compositor has sampled the content transform for this
|
||||
// frame.
|
||||
TimeStamp mLastSampleTime;
|
||||
// The last time a touch event came through on the UI thread.
|
||||
PRInt32 mLastEventTime;
|
||||
// The last time a repaint has been requested from Gecko.
|
||||
PRInt32 mLastRepaint;
|
||||
|
||||
// Stores the previous focus point if there is a pinch gesture happening. Used
|
||||
// to allow panning by moving multiple fingers (thus moving the focus point).
|
||||
nsIntPoint mLastZoomFocus;
|
||||
PanZoomState mState;
|
||||
int mDPI;
|
||||
|
||||
friend class Axis;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // mozilla_layers_PanZoomController_h
|
|
@ -0,0 +1,295 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Axis.h"
|
||||
#include "AsyncPanZoomController.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
static const float EPSILON = 0.0001;
|
||||
|
||||
/**
|
||||
* Milliseconds per frame, used to judge how much displacement should have
|
||||
* happened every frame based on the velocity calculated from touch events.
|
||||
*/
|
||||
static const float MS_PER_FRAME = 1000.0f / 60.0f;
|
||||
|
||||
/**
|
||||
* Maximum acceleration that can happen between two frames. Velocity is
|
||||
* throttled if it's above this. This may happen if a time delta is very low,
|
||||
* or we get a touch point very far away from the previous position for some
|
||||
* reason.
|
||||
*/
|
||||
static const float MAX_EVENT_ACCELERATION = 12;
|
||||
|
||||
/**
|
||||
* Amount of friction applied during flings when going above
|
||||
* VELOCITY_THRESHOLD.
|
||||
*/
|
||||
static const float FLING_FRICTION_FAST = 0.010;
|
||||
|
||||
/**
|
||||
* Amount of friction applied during flings when going below
|
||||
* VELOCITY_THRESHOLD.
|
||||
*/
|
||||
static const float FLING_FRICTION_SLOW = 0.008;
|
||||
|
||||
/**
|
||||
* Maximum velocity before fling friction increases.
|
||||
*/
|
||||
static const float VELOCITY_THRESHOLD = 10;
|
||||
|
||||
/**
|
||||
* When flinging, if the velocity goes below this number, we just stop the
|
||||
* animation completely. This is to prevent asymptotically approaching 0
|
||||
* velocity and rerendering unnecessarily.
|
||||
*/
|
||||
static const float FLING_STOPPED_THRESHOLD = 0.1f;
|
||||
|
||||
Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: mPos(0.0f),
|
||||
mVelocity(0.0f),
|
||||
mAsyncPanZoomController(aAsyncPanZoomController)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Axis::UpdateWithTouchAtDevicePoint(PRInt32 aPos, PRInt32 aTimeDelta) {
|
||||
float newVelocity = MS_PER_FRAME * (mPos - aPos) / aTimeDelta;
|
||||
|
||||
bool curVelocityIsLow = fabsf(newVelocity) < 1.0f;
|
||||
bool directionChange = (mVelocity > 0) != (newVelocity != 0);
|
||||
|
||||
// If a direction change has happened, or the current velocity due to this new
|
||||
// touch is relatively low, then just apply it. If not, throttle it.
|
||||
if (curVelocityIsLow || (directionChange && fabs(newVelocity) - EPSILON <= 0.0f)) {
|
||||
mVelocity = newVelocity;
|
||||
} else {
|
||||
float maxChange = fabsf(mVelocity * aTimeDelta * MAX_EVENT_ACCELERATION);
|
||||
mVelocity = NS_MIN(mVelocity + maxChange, NS_MAX(mVelocity - maxChange, newVelocity));
|
||||
}
|
||||
|
||||
mVelocity = newVelocity;
|
||||
mPos = aPos;
|
||||
}
|
||||
|
||||
void Axis::StartTouch(PRInt32 aPos) {
|
||||
mStartPos = aPos;
|
||||
mPos = aPos;
|
||||
mVelocity = 0.0f;
|
||||
}
|
||||
|
||||
PRInt32 Axis::UpdateAndGetDisplacement(float aScale) {
|
||||
PRInt32 displacement = NS_lround(mVelocity * aScale);
|
||||
// If this displacement will cause an overscroll, throttle it. Can potentially
|
||||
// bring it to 0 even if the velocity is high.
|
||||
if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) {
|
||||
displacement -= DisplacementWillOverscrollAmount(displacement);
|
||||
}
|
||||
return displacement;
|
||||
}
|
||||
|
||||
float Axis::PanDistance() {
|
||||
return fabsf(mPos - mStartPos);
|
||||
}
|
||||
|
||||
void Axis::StopTouch() {
|
||||
mVelocity = 0.0f;
|
||||
}
|
||||
|
||||
bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
|
||||
if (fabsf(mVelocity) <= FLING_STOPPED_THRESHOLD) {
|
||||
// If the velocity is very low, just set it to 0 and stop the fling,
|
||||
// otherwise we'll just asymptotically approach 0 and the user won't
|
||||
// actually see any changes.
|
||||
mVelocity = 0.0f;
|
||||
return false;
|
||||
} else if (fabsf(mVelocity) >= VELOCITY_THRESHOLD) {
|
||||
mVelocity *= 1.0f - FLING_FRICTION_FAST * aDelta.ToMilliseconds();
|
||||
} else {
|
||||
mVelocity *= 1.0f - FLING_FRICTION_SLOW * aDelta.ToMilliseconds();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Axis::Overscroll Axis::GetOverscroll() {
|
||||
// If the current pan takes the viewport to the left of or above the current
|
||||
// page rect.
|
||||
bool minus = GetOrigin() < GetPageStart();
|
||||
// If the current pan takes the viewport to the right of or below the current
|
||||
// page rect.
|
||||
bool plus = GetViewportEnd() > GetPageEnd();
|
||||
if (minus && plus) {
|
||||
return OVERSCROLL_BOTH;
|
||||
}
|
||||
if (minus) {
|
||||
return OVERSCROLL_MINUS;
|
||||
}
|
||||
if (plus) {
|
||||
return OVERSCROLL_PLUS;
|
||||
}
|
||||
return OVERSCROLL_NONE;
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetExcess() {
|
||||
switch (GetOverscroll()) {
|
||||
case OVERSCROLL_MINUS: return GetOrigin() - GetPageStart();
|
||||
case OVERSCROLL_PLUS: return GetViewportEnd() - GetPageEnd();
|
||||
case OVERSCROLL_BOTH: return (GetViewportEnd() - GetPageEnd()) + (GetPageStart() - GetOrigin());
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Axis::Overscroll Axis::DisplacementWillOverscroll(PRInt32 aDisplacement) {
|
||||
// If the current pan plus a displacement takes the viewport to the left of or
|
||||
// above the current page rect.
|
||||
bool minus = GetOrigin() + aDisplacement < GetPageStart();
|
||||
// If the current pan plus a displacement takes the viewport to the right of or
|
||||
// below the current page rect.
|
||||
bool plus = GetViewportEnd() + aDisplacement > GetPageEnd();
|
||||
if (minus && plus) {
|
||||
return OVERSCROLL_BOTH;
|
||||
}
|
||||
if (minus) {
|
||||
return OVERSCROLL_MINUS;
|
||||
}
|
||||
if (plus) {
|
||||
return OVERSCROLL_PLUS;
|
||||
}
|
||||
return OVERSCROLL_NONE;
|
||||
}
|
||||
|
||||
PRInt32 Axis::DisplacementWillOverscrollAmount(PRInt32 aDisplacement) {
|
||||
switch (DisplacementWillOverscroll(aDisplacement)) {
|
||||
case OVERSCROLL_MINUS: return (GetOrigin() + aDisplacement) - GetPageStart();
|
||||
case OVERSCROLL_PLUS: return (GetViewportEnd() + aDisplacement) - GetPageEnd();
|
||||
// Don't handle overscrolled in both directions; a displacement can't cause
|
||||
// this, it must have already been zoomed out too far.
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Axis::Overscroll Axis::ScaleWillOverscroll(float aScale, PRInt32 aFocus) {
|
||||
PRInt32 originAfterScale = NS_lround((GetOrigin() + aFocus) * aScale - aFocus);
|
||||
|
||||
bool both = ScaleWillOverscrollBothSides(aScale);
|
||||
bool minus = originAfterScale < NS_lround(GetPageStart() * aScale);
|
||||
bool plus = (originAfterScale + GetViewportLength()) > NS_lround(GetPageEnd() * aScale);
|
||||
|
||||
if ((minus && plus) || both) {
|
||||
return OVERSCROLL_BOTH;
|
||||
}
|
||||
if (minus) {
|
||||
return OVERSCROLL_MINUS;
|
||||
}
|
||||
if (plus) {
|
||||
return OVERSCROLL_PLUS;
|
||||
}
|
||||
return OVERSCROLL_NONE;
|
||||
}
|
||||
|
||||
PRInt32 Axis::ScaleWillOverscrollAmount(float aScale, PRInt32 aFocus) {
|
||||
PRInt32 originAfterScale = NS_lround((GetOrigin() + aFocus) * aScale - aFocus);
|
||||
switch (ScaleWillOverscroll(aScale, aFocus)) {
|
||||
case OVERSCROLL_MINUS: return originAfterScale - NS_lround(GetPageStart() * aScale);
|
||||
case OVERSCROLL_PLUS: return (originAfterScale + GetViewportLength()) - NS_lround(GetPageEnd() * aScale);
|
||||
// Don't handle OVERSCROLL_BOTH. Client code is expected to deal with it.
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
float Axis::GetVelocity() {
|
||||
return mVelocity;
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetViewportEnd() {
|
||||
return GetOrigin() + GetViewportLength();
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetPageEnd() {
|
||||
return GetPageStart() + GetPageLength();
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetOrigin() {
|
||||
nsIntPoint origin = mAsyncPanZoomController->GetFrameMetrics().mViewportScrollOffset;
|
||||
return GetPointOffset(origin);
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetViewportLength() {
|
||||
nsIntRect viewport = mAsyncPanZoomController->GetFrameMetrics().mViewport;
|
||||
return GetRectLength(viewport);
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetPageStart() {
|
||||
nsIntRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mContentRect;
|
||||
return GetRectOffset(pageRect);
|
||||
}
|
||||
|
||||
PRInt32 Axis::GetPageLength() {
|
||||
nsIntRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mContentRect;
|
||||
return GetRectLength(pageRect);
|
||||
}
|
||||
|
||||
bool Axis::ScaleWillOverscrollBothSides(float aScale) {
|
||||
const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
|
||||
|
||||
float currentScale = metrics.mResolution.width;
|
||||
gfx::Rect cssContentRect = metrics.mCSSContentRect;
|
||||
cssContentRect.ScaleRoundIn(currentScale * aScale);
|
||||
|
||||
nsIntRect contentRect = nsIntRect(cssContentRect.x,
|
||||
cssContentRect.y,
|
||||
cssContentRect.width,
|
||||
cssContentRect.height);
|
||||
|
||||
return GetRectLength(contentRect) < GetRectLength(metrics.mViewport);
|
||||
}
|
||||
|
||||
AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: Axis(aAsyncPanZoomController)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PRInt32 AxisX::GetPointOffset(const nsIntPoint& aPoint)
|
||||
{
|
||||
return aPoint.x;
|
||||
}
|
||||
|
||||
PRInt32 AxisX::GetRectLength(const nsIntRect& aRect)
|
||||
{
|
||||
return aRect.width;
|
||||
}
|
||||
|
||||
PRInt32 AxisX::GetRectOffset(const nsIntRect& aRect)
|
||||
{
|
||||
return aRect.x;
|
||||
}
|
||||
|
||||
AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: Axis(aAsyncPanZoomController)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PRInt32 AxisY::GetPointOffset(const nsIntPoint& aPoint)
|
||||
{
|
||||
return aPoint.y;
|
||||
}
|
||||
|
||||
PRInt32 AxisY::GetRectLength(const nsIntRect& aRect)
|
||||
{
|
||||
return aRect.height;
|
||||
}
|
||||
|
||||
PRInt32 AxisY::GetRectOffset(const nsIntRect& aRect)
|
||||
{
|
||||
return aRect.y;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_layers_Axis_h
|
||||
#define mozilla_layers_Axis_h
|
||||
|
||||
#include "nsGUIEvent.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class AsyncPanZoomController;
|
||||
|
||||
/**
|
||||
* Helper class to maintain each axis of movement (X,Y) for panning and zooming.
|
||||
* Note that everything here is specific to one axis; that is, the X axis knows
|
||||
* nothing about the Y axis and vice versa.
|
||||
*/
|
||||
class Axis {
|
||||
public:
|
||||
Axis(AsyncPanZoomController* aAsyncPanZoomController);
|
||||
|
||||
enum Overscroll {
|
||||
// Overscroll is not happening at all.
|
||||
OVERSCROLL_NONE = 0,
|
||||
// Overscroll is happening in the negative direction. This means either to
|
||||
// the left or to the top depending on the axis.
|
||||
OVERSCROLL_MINUS,
|
||||
// Overscroll is happening in the positive direction. This means either to
|
||||
// the right or to the bottom depending on the axis.
|
||||
OVERSCROLL_PLUS,
|
||||
// Overscroll is happening both ways. This only means something when the
|
||||
// page is scaled out to a smaller size than the viewport.
|
||||
OVERSCROLL_BOTH
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify this Axis that a new touch has been received, including a time delta
|
||||
* indicating how long it has been since the previous one. This triggers a
|
||||
* recalculation of velocity.
|
||||
*/
|
||||
void UpdateWithTouchAtDevicePoint(PRInt32 aPos, PRInt32 aTimeDelta);
|
||||
|
||||
/**
|
||||
* Notify this Axis that a touch has begun, i.e. the user has put their finger
|
||||
* on the screen but has not yet tried to pan.
|
||||
*/
|
||||
void StartTouch(PRInt32 aPos);
|
||||
|
||||
/**
|
||||
* Notify this Axis that a touch has ended. Useful for stopping flings when a
|
||||
* user puts their finger down in the middle of one (i.e. to stop a previous
|
||||
* touch including its fling so that a new one can take its place).
|
||||
*/
|
||||
void StopTouch();
|
||||
|
||||
/**
|
||||
* Gets displacement that should have happened since the previous touch.
|
||||
* Note: Does not reset the displacement. It gets recalculated on the next
|
||||
* updateWithTouchAtDevicePoint(), however it is not safe to assume this will
|
||||
* be the same on every call. This also checks for page boundaries and will
|
||||
* return an adjusted displacement to prevent the viewport from overscrolling
|
||||
* the page rect. An example of where this might matter is when you call it,
|
||||
* apply a displacement that takes you to the boundary of the page, then call
|
||||
* it again. The result will be different in this case.
|
||||
*/
|
||||
PRInt32 UpdateAndGetDisplacement(float aScale);
|
||||
|
||||
/**
|
||||
* Gets the distance between the starting position of the touch supplied in
|
||||
* startTouch() and the current touch from the last
|
||||
* updateWithTouchAtDevicePoint().
|
||||
*/
|
||||
float PanDistance();
|
||||
|
||||
/**
|
||||
* Applies friction during a fling, or cancels the fling if the velocity is
|
||||
* too low. Returns true if the fling should continue to another frame, or
|
||||
* false if it should end. |aDelta| is the amount of time that has passed
|
||||
* since the last time friction was applied.
|
||||
*/
|
||||
bool FlingApplyFrictionOrCancel(const TimeDuration& aDelta);
|
||||
|
||||
/**
|
||||
* Gets the overscroll state of the axis in its current position.
|
||||
*/
|
||||
Overscroll GetOverscroll();
|
||||
|
||||
/**
|
||||
* If there is overscroll, returns the amount. Sign depends on in what
|
||||
* direction it is overscrolling. Positive excess means that it is
|
||||
* overscrolling in the positive direction, whereas negative excess means
|
||||
* that it is overscrolling in the negative direction. If there is overscroll
|
||||
* in both directions, this returns 0; it assumes that you check
|
||||
* GetOverscroll() first.
|
||||
*/
|
||||
PRInt32 GetExcess();
|
||||
|
||||
/**
|
||||
* Gets the raw velocity of this axis at this moment.
|
||||
*/
|
||||
float GetVelocity();
|
||||
|
||||
/**
|
||||
* Gets the overscroll state of the axis given an additional displacement.
|
||||
* That is to say, if the given displacement is applied, this will tell you
|
||||
* whether or not it will overscroll, and in what direction.
|
||||
*/
|
||||
Overscroll DisplacementWillOverscroll(PRInt32 aDisplacement);
|
||||
|
||||
/**
|
||||
* If a displacement will overscroll the axis, this returns the amount and in
|
||||
* what direction. Similar to getExcess() but takes a displacement to apply.
|
||||
*/
|
||||
PRInt32 DisplacementWillOverscrollAmount(PRInt32 aDisplacement);
|
||||
|
||||
/**
|
||||
* Gets the overscroll state of the axis given a scaling of the page. That is
|
||||
* to say, if the given scale is applied, this will tell you whether or not
|
||||
* it will overscroll, and in what direction.
|
||||
*
|
||||
* |aFocus| is the point at which the scale is focused at. We will offset the
|
||||
* scroll offset in such a way that it remains in the same place on the page
|
||||
* relative.
|
||||
*/
|
||||
Overscroll ScaleWillOverscroll(float aScale, PRInt32 aFocus);
|
||||
|
||||
/**
|
||||
* If a scale will overscroll the axis, this returns the amount and in what
|
||||
* direction. Similar to getExcess() but takes a displacement to apply.
|
||||
*
|
||||
* |aFocus| is the point at which the scale is focused at. We will offset the
|
||||
* scroll offset in such a way that it remains in the same place on the page
|
||||
* relative.
|
||||
*/
|
||||
PRInt32 ScaleWillOverscrollAmount(float aScale, PRInt32 aFocus);
|
||||
|
||||
/**
|
||||
* Checks if an axis will overscroll in both directions by computing the
|
||||
* content rect and checking that its height/width (depending on the axis)
|
||||
* does not overextend past the viewport.
|
||||
*
|
||||
* This gets called by ScaleWillOverscroll().
|
||||
*/
|
||||
bool ScaleWillOverscrollBothSides(float aScale);
|
||||
|
||||
PRInt32 GetOrigin();
|
||||
PRInt32 GetViewportLength();
|
||||
PRInt32 GetPageStart();
|
||||
PRInt32 GetPageLength();
|
||||
PRInt32 GetViewportEnd();
|
||||
PRInt32 GetPageEnd();
|
||||
|
||||
virtual PRInt32 GetPointOffset(const nsIntPoint& aPoint) = 0;
|
||||
virtual PRInt32 GetRectLength(const nsIntRect& aRect) = 0;
|
||||
virtual PRInt32 GetRectOffset(const nsIntRect& aRect) = 0;
|
||||
|
||||
protected:
|
||||
PRInt32 mPos;
|
||||
PRInt32 mStartPos;
|
||||
float mVelocity;
|
||||
nsRefPtr<AsyncPanZoomController> mAsyncPanZoomController;
|
||||
};
|
||||
|
||||
class AxisX : public Axis {
|
||||
public:
|
||||
AxisX(AsyncPanZoomController* mAsyncPanZoomController);
|
||||
virtual PRInt32 GetPointOffset(const nsIntPoint& aPoint);
|
||||
virtual PRInt32 GetRectLength(const nsIntRect& aRect);
|
||||
virtual PRInt32 GetRectOffset(const nsIntRect& aRect);
|
||||
};
|
||||
|
||||
class AxisY : public Axis {
|
||||
public:
|
||||
AxisY(AsyncPanZoomController* mAsyncPanZoomController);
|
||||
virtual PRInt32 GetPointOffset(const nsIntPoint& aPoint);
|
||||
virtual PRInt32 GetRectLength(const nsIntRect& aRect);
|
||||
virtual PRInt32 GetRectOffset(const nsIntRect& aRect);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_layers_GeckoContentController_h
|
||||
#define mozilla_layers_GeckoContentController_h
|
||||
|
||||
#include "Layers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class GeckoContentController {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoContentController)
|
||||
|
||||
/**
|
||||
* Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
|
||||
* Implementations per-platform are responsible for actually handling this.
|
||||
*/
|
||||
virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // mozilla_layers_GeckoContentController_h
|
|
@ -0,0 +1,208 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GestureEventListener.h"
|
||||
#include "AsyncPanZoomController.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: mAsyncPanZoomController(aAsyncPanZoomController),
|
||||
mState(NoGesture)
|
||||
{
|
||||
}
|
||||
|
||||
GestureEventListener::~GestureEventListener()
|
||||
{
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
|
||||
{
|
||||
if (aEvent.mInputType != MULTITOUCH_INPUT) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
const MultiTouchInput& event = static_cast<const MultiTouchInput&>(aEvent);
|
||||
switch (event.mType)
|
||||
{
|
||||
case MultiTouchInput::MULTITOUCH_START:
|
||||
case MultiTouchInput::MULTITOUCH_ENTER: {
|
||||
for (size_t i = 0; i < event.mTouches.Length(); i++) {
|
||||
bool foundAlreadyExistingTouch = false;
|
||||
for (size_t j = 0; j < mTouches.Length(); j++) {
|
||||
if (mTouches[j].mIdentifier == event.mTouches[i].mIdentifier) {
|
||||
foundAlreadyExistingTouch = true;
|
||||
}
|
||||
}
|
||||
|
||||
NS_WARN_IF_FALSE(!foundAlreadyExistingTouch, "Tried to add a touch that already exists");
|
||||
|
||||
// If we didn't find a touch in our list that matches this, then add it.
|
||||
// If it already existed, we don't want to add it twice because that
|
||||
// messes with our touch move/end code.
|
||||
if (!foundAlreadyExistingTouch) {
|
||||
mTouches.AppendElement(event.mTouches[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (mTouches.Length() == 2) {
|
||||
// Another finger has been added; it can't be a tap anymore.
|
||||
HandleTapCancel(event);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MultiTouchInput::MULTITOUCH_MOVE: {
|
||||
// If we move at all, just bail out of the tap. We need to change this so
|
||||
// that there's some tolerance in the future.
|
||||
HandleTapCancel(event);
|
||||
|
||||
bool foundAlreadyExistingTouch = false;
|
||||
for (size_t i = 0; i < mTouches.Length(); i++) {
|
||||
for (size_t j = 0; j < event.mTouches.Length(); j++) {
|
||||
if (mTouches[i].mIdentifier == event.mTouches[j].mIdentifier) {
|
||||
foundAlreadyExistingTouch = true;
|
||||
mTouches[i] = event.mTouches[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch moved, but not in list");
|
||||
|
||||
break;
|
||||
}
|
||||
case MultiTouchInput::MULTITOUCH_END:
|
||||
case MultiTouchInput::MULTITOUCH_LEAVE: {
|
||||
bool foundAlreadyExistingTouch = false;
|
||||
for (size_t i = 0; i < event.mTouches.Length() && !foundAlreadyExistingTouch; i++) {
|
||||
for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
|
||||
if (event.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
|
||||
foundAlreadyExistingTouch = true;
|
||||
mTouches.RemoveElementAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");
|
||||
|
||||
if (event.mTime - mTouchStartTime <= MAX_TAP_TIME) {
|
||||
// XXX: Incorrect use of the tap event. In the future, we want to send this
|
||||
// on NS_TOUCH_END, then have a short timer afterwards which sends
|
||||
// SingleTapConfirmed. Since we don't have double taps yet, this is fine for
|
||||
// now.
|
||||
if (HandleSingleTapUpEvent(event) == nsEventStatus_eConsumeNoDefault) {
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
if (HandleSingleTapConfirmedEvent(event) == nsEventStatus_eConsumeNoDefault) {
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MultiTouchInput::MULTITOUCH_CANCEL:
|
||||
// This gets called if there's a touch that has to bail for weird reasons
|
||||
// like pinching and then moving away from the window that the pinch was
|
||||
// started in without letting go of the screen.
|
||||
HandlePinchGestureEvent(event, true);
|
||||
break;
|
||||
}
|
||||
|
||||
return HandlePinchGestureEvent(event, false);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent, bool aClearTouches)
|
||||
{
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
if (mTouches.Length() > 1 && !aClearTouches) {
|
||||
const nsIntPoint& firstTouch = mTouches[0].mScreenPoint,
|
||||
secondTouch = mTouches[mTouches.Length() - 1].mScreenPoint;
|
||||
nsIntPoint focusPoint =
|
||||
nsIntPoint((firstTouch.x + secondTouch.x)/2,
|
||||
(firstTouch.y + secondTouch.y)/2);
|
||||
float currentSpan =
|
||||
float(NS_hypot(firstTouch.x - secondTouch.x,
|
||||
firstTouch.y - secondTouch.y));
|
||||
|
||||
if (mState == NoGesture) {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
|
||||
aEvent.mTime,
|
||||
focusPoint,
|
||||
currentSpan,
|
||||
currentSpan);
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
|
||||
mState = InPinchGesture;
|
||||
} else {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
aEvent.mTime,
|
||||
focusPoint,
|
||||
currentSpan,
|
||||
mPreviousSpan);
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
}
|
||||
|
||||
mPreviousSpan = currentSpan;
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mState == InPinchGesture) {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
|
||||
aEvent.mTime,
|
||||
mTouches[0].mScreenPoint,
|
||||
1.0f,
|
||||
1.0f);
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
|
||||
mState = NoGesture;
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
if (aClearTouches) {
|
||||
mTouches.Clear();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleSingleTapUpEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_UP, aEvent.mTime, aEvent.mTouches[0].mScreenPoint);
|
||||
mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleSingleTapConfirmedEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_CONFIRMED, aEvent.mTime, aEvent.mTouches[0].mScreenPoint);
|
||||
mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEvent)
|
||||
{
|
||||
// XXX: In the future we will have to actually send a cancel notification to
|
||||
// Gecko, but for now since we're doing both the "SingleUp" and
|
||||
// "SingleConfirmed" notifications together, there's no need to cancel either
|
||||
// one.
|
||||
mTouchStartTime = 0;
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
AsyncPanZoomController* GestureEventListener::GetAsyncPanZoomController() {
|
||||
return mAsyncPanZoomController;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_layers_GestureEventListener_h
|
||||
#define mozilla_layers_GestureEventListener_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "InputData.h"
|
||||
#include "Axis.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* Platform-non-specific, generalized gesture event listener. This class
|
||||
* intercepts all touches events on their way to AsyncPanZoomController and
|
||||
* determines whether or not they are part of a gesture.
|
||||
*
|
||||
* For example, seeing that two fingers are on the screen means that the user
|
||||
* wants to do a pinch gesture, so we don't forward the touches along to
|
||||
* AsyncPanZoomController since it will think that they are just trying to pan
|
||||
* the screen. Instead, we generate a PinchGestureInput and send that. If the
|
||||
* touch event is not part of a gesture, we just return nsEventStatus_eIgnore
|
||||
* and AsyncPanZoomController is expected to handle it.
|
||||
*
|
||||
* Android doesn't use this class because it has its own built-in gesture event
|
||||
* listeners that should generally be preferred.
|
||||
*/
|
||||
class GestureEventListener {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GestureEventListener)
|
||||
|
||||
GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController);
|
||||
~GestureEventListener();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// These methods must only be called on the controller/UI thread.
|
||||
//
|
||||
|
||||
/**
|
||||
* General input handler for a touch event. If the touch event is not a part
|
||||
* of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
|
||||
* it gets consumed here and never forwarded along.
|
||||
*/
|
||||
nsEventStatus HandleInputEvent(const InputData& aEvent);
|
||||
|
||||
/**
|
||||
* Returns the AsyncPanZoomController stored on this class and used for
|
||||
* callbacks.
|
||||
*/
|
||||
AsyncPanZoomController* GetAsyncPanZoomController();
|
||||
|
||||
protected:
|
||||
enum GestureState {
|
||||
NoGesture = 0,
|
||||
InPinchGesture
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum time for a touch on the screen and corresponding lift of the finger
|
||||
* to be considered a tap.
|
||||
*/
|
||||
enum { MAX_TAP_TIME = 500 };
|
||||
|
||||
/**
|
||||
* Attempts to handle the event as a pinch event. If it is not a pinch event,
|
||||
* then we simply tell the next consumer to consume the event instead.
|
||||
*
|
||||
* |aClearTouches| marks whether or not to terminate any pinch currently
|
||||
* happening.
|
||||
*/
|
||||
nsEventStatus HandlePinchGestureEvent(const MultiTouchInput& aEvent, bool aClearTouches);
|
||||
|
||||
/**
|
||||
* Attempts to handle the event as a single tap event, which highlights links
|
||||
* before opening them. In general, this will not attempt to block the touch
|
||||
* event from being passed along to AsyncPanZoomController since APZC needs to
|
||||
* know about touches ending (and we only know if a touch was a tap once it
|
||||
* ends).
|
||||
*/
|
||||
nsEventStatus HandleSingleTapUpEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a single tap confirmation. This is what will actually
|
||||
* open links, etc. In general, this will not attempt to block the touch event
|
||||
* from being passed along to AsyncPanZoomController since APZC needs to know
|
||||
* about touches ending (and we only know if a touch was a tap once it ends).
|
||||
*/
|
||||
nsEventStatus HandleSingleTapConfirmedEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a tap event cancellation. This happens when we think
|
||||
* something was a tap but it actually wasn't. In general, this will not
|
||||
* attempt to block the touch event from being passed along to
|
||||
* AsyncPanZoomController since APZC needs to know about touches ending (and
|
||||
* we only know if a touch was a tap once it ends).
|
||||
*/
|
||||
nsEventStatus HandleTapCancel(const MultiTouchInput& aEvent);
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> mAsyncPanZoomController;
|
||||
|
||||
/**
|
||||
* Array containing all active touches. When a touch happens it, gets added to
|
||||
* this array, even if we choose not to handle it. When it ends, we remove it.
|
||||
*/
|
||||
nsTArray<SingleTouchData> mTouches;
|
||||
GestureState mState;
|
||||
|
||||
/**
|
||||
* Previous span calculated for the purposes of setting inside a
|
||||
* PinchGestureInput.
|
||||
*/
|
||||
float mPreviousSpan;
|
||||
|
||||
/**
|
||||
* Stores the time a touch started, used for detecting a tap gesture. Only
|
||||
* valid when there's exactly one touch in mTouches.
|
||||
*/
|
||||
PRUint64 mTouchStartTime;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,325 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef InputData_h__
|
||||
#define InputData_h__
|
||||
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsDOMTouchEvent.h"
|
||||
#include "nsDebug.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
enum InputType
|
||||
{
|
||||
MULTITOUCH_INPUT,
|
||||
PINCHGESTURE_INPUT,
|
||||
TAPGESTURE_INPUT
|
||||
};
|
||||
|
||||
class MultiTouchInput;
|
||||
class PinchGestureInput;
|
||||
class TapGestureInput;
|
||||
|
||||
// This looks unnecessary now, but as we add more and more classes that derive
|
||||
// from InputType (eventually probably almost as many as nsGUIEvent.h has), it
|
||||
// will be more and more clear what's going on with a macro that shortens the
|
||||
// definition of the RTTI functions.
|
||||
#define INPUTDATA_AS_CHILD_TYPE(type, enumID) \
|
||||
const type& As##type() const \
|
||||
{ \
|
||||
NS_ABORT_IF_FALSE(mInputType == enumID, "Invalid cast of InputData."); \
|
||||
return (const type&) *this; \
|
||||
}
|
||||
|
||||
/** Base input data class. Should never be instantiated. */
|
||||
class InputData
|
||||
{
|
||||
public:
|
||||
InputType mInputType;
|
||||
// Time in milliseconds that this data is relevant to. This only really
|
||||
// matters when this data is used as an event. We use PRUint32 instead of
|
||||
// TimeStamp because it is easier to convert from nsInputEvent. The time is
|
||||
// platform-specific but it in the case of B2G and Fennec it is since startup.
|
||||
PRUint32 mTime;
|
||||
|
||||
INPUTDATA_AS_CHILD_TYPE(MultiTouchInput, MULTITOUCH_INPUT)
|
||||
INPUTDATA_AS_CHILD_TYPE(PinchGestureInput, PINCHGESTURE_INPUT)
|
||||
INPUTDATA_AS_CHILD_TYPE(TapGestureInput, TAPGESTURE_INPUT)
|
||||
|
||||
protected:
|
||||
InputData(InputType aInputType, PRUint32 aTime)
|
||||
: mInputType(aInputType),
|
||||
mTime(aTime)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Data container for a single touch input. Similar to nsDOMTouch, but used in
|
||||
* off-main-thread situations. This is more for just storing touch data, whereas
|
||||
* nsDOMTouch derives from nsIDOMTouch so it is more useful for dispatching
|
||||
* through the DOM (which can only happen on the main thread). nsDOMTouch also
|
||||
* bears the problem of storing pointers to nsIWidget instances which can only
|
||||
* be used on the main thread, so if instead we used nsDOMTouch and ever set
|
||||
* these pointers off-main-thread, Bad Things Can Happen(tm).
|
||||
*
|
||||
* Note that this doesn't inherit from InputData because this itself is not an
|
||||
* event. It is only a container/struct that should have any number of instances
|
||||
* within a MultiTouchInput.
|
||||
*
|
||||
* fixme/bug 775746: Make nsDOMTouch inherit from this class.
|
||||
*/
|
||||
class SingleTouchData
|
||||
{
|
||||
public:
|
||||
SingleTouchData(PRInt32 aIdentifier,
|
||||
nsIntPoint aScreenPoint,
|
||||
nsIntPoint aRadius,
|
||||
float aRotationAngle,
|
||||
float aForce)
|
||||
: mIdentifier(aIdentifier),
|
||||
mScreenPoint(aScreenPoint),
|
||||
mRadius(aRadius),
|
||||
mRotationAngle(aRotationAngle),
|
||||
mForce(aForce)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
// A unique number assigned to each SingleTouchData within a MultiTouchInput so
|
||||
// that they can be easily distinguished when handling a touch start/move/end.
|
||||
PRInt32 mIdentifier;
|
||||
|
||||
// Point on the screen that the touch hit, in device pixels. They are
|
||||
// coordinates on the screen.
|
||||
nsIntPoint mScreenPoint;
|
||||
|
||||
// Radius that the touch covers, i.e. if you're using your thumb it will
|
||||
// probably be larger than using your pinky, even with the same force.
|
||||
// Radius can be different along x and y. For example, if you press down with
|
||||
// your entire finger vertically, the y radius will be much larger than the x
|
||||
// radius.
|
||||
nsIntPoint mRadius;
|
||||
|
||||
float mRotationAngle;
|
||||
|
||||
// How hard the screen is being pressed.
|
||||
float mForce;
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to nsTouchEvent, but for use off-main-thread. Also only stores a
|
||||
* screen touch point instead of the many different coordinate spaces nsTouchEvent
|
||||
* stores its touch point in. This includes a way to initialize itself from an
|
||||
* nsTouchEvent by copying all relevant data over. Note that this copying from
|
||||
* nsTouchEvent functionality can only be used on the main thread.
|
||||
*
|
||||
* Stores an array of SingleTouchData.
|
||||
*/
|
||||
class MultiTouchInput : public InputData
|
||||
{
|
||||
public:
|
||||
enum MultiTouchType
|
||||
{
|
||||
MULTITOUCH_START,
|
||||
MULTITOUCH_MOVE,
|
||||
MULTITOUCH_END,
|
||||
MULTITOUCH_ENTER,
|
||||
MULTITOUCH_LEAVE,
|
||||
MULTITOUCH_CANCEL
|
||||
};
|
||||
|
||||
MultiTouchInput(MultiTouchType aType, PRUint32 aTime)
|
||||
: InputData(MULTITOUCH_INPUT, aTime),
|
||||
mType(aType)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
MultiTouchInput(const nsTouchEvent& aTouchEvent)
|
||||
: InputData(MULTITOUCH_INPUT, aTouchEvent.time)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(),
|
||||
"Can only copy from nsTouchEvent on main thread");
|
||||
|
||||
switch (aTouchEvent.message) {
|
||||
case NS_TOUCH_START:
|
||||
mType = MULTITOUCH_START;
|
||||
break;
|
||||
case NS_TOUCH_MOVE:
|
||||
mType = MULTITOUCH_MOVE;
|
||||
break;
|
||||
case NS_TOUCH_END:
|
||||
mType = MULTITOUCH_END;
|
||||
break;
|
||||
case NS_TOUCH_ENTER:
|
||||
mType = MULTITOUCH_ENTER;
|
||||
break;
|
||||
case NS_TOUCH_LEAVE:
|
||||
mType = MULTITOUCH_LEAVE;
|
||||
break;
|
||||
case NS_TOUCH_CANCEL:
|
||||
mType = MULTITOUCH_CANCEL;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Did not assign a type to a MultiTouchInput");
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aTouchEvent.touches.Length(); i++) {
|
||||
nsDOMTouch* domTouch = (nsDOMTouch*)(aTouchEvent.touches[i].get());
|
||||
|
||||
// Extract data from weird interfaces.
|
||||
PRInt32 identifier, radiusX, radiusY;
|
||||
float rotationAngle, force;
|
||||
domTouch->GetIdentifier(&identifier);
|
||||
domTouch->GetRadiusX(&radiusX);
|
||||
domTouch->GetRadiusY(&radiusY);
|
||||
domTouch->GetRotationAngle(&rotationAngle);
|
||||
domTouch->GetForce(&force);
|
||||
|
||||
SingleTouchData data(identifier,
|
||||
domTouch->mRefPoint,
|
||||
nsIntPoint(radiusX, radiusY),
|
||||
rotationAngle,
|
||||
force);
|
||||
|
||||
mTouches.AppendElement(data);
|
||||
}
|
||||
}
|
||||
|
||||
// This conversion from nsMouseEvent to MultiTouchInput is needed because on
|
||||
// the B2G emulator we can only receive mouse events, but we need to be able
|
||||
// to pan correctly. To do this, we convert the events into a format that the
|
||||
// panning code can handle. This code is very limited and only supports
|
||||
// SingleTouchData. It also sends garbage for the identifier, radius, force
|
||||
// and rotation angle.
|
||||
MultiTouchInput(const nsMouseEvent& aMouseEvent)
|
||||
: InputData(MULTITOUCH_INPUT, aMouseEvent.time)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(),
|
||||
"Can only copy from nsMouseEvent on main thread");
|
||||
switch (aMouseEvent.message) {
|
||||
case NS_MOUSE_BUTTON_DOWN:
|
||||
mType = MULTITOUCH_START;
|
||||
break;
|
||||
case NS_MOUSE_MOVE:
|
||||
mType = MULTITOUCH_MOVE;
|
||||
break;
|
||||
case NS_MOUSE_BUTTON_UP:
|
||||
mType = MULTITOUCH_END;
|
||||
break;
|
||||
// The mouse pointer has been interrupted in an implementation-specific
|
||||
// manner, such as a synchronous event or action cancelling the touch, or a
|
||||
// touch point leaving the document window and going into a non-document
|
||||
// area capable of handling user interactions.
|
||||
case NS_MOUSE_EXIT:
|
||||
mType = MULTITOUCH_CANCEL;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Did not assign a type to a MultiTouchInput");
|
||||
break;
|
||||
}
|
||||
|
||||
mTouches.AppendElement(SingleTouchData(0,
|
||||
aMouseEvent.refPoint,
|
||||
nsIntPoint(1, 1),
|
||||
180.0f,
|
||||
1.0f));
|
||||
}
|
||||
|
||||
MultiTouchType mType;
|
||||
nsTArray<SingleTouchData> mTouches;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulation class for pinch events. In general, these will be generated by
|
||||
* a gesture listener by looking at SingleTouchData/MultiTouchInput instances and
|
||||
* determining whether or not the user was trying to do a gesture.
|
||||
*/
|
||||
class PinchGestureInput : public InputData
|
||||
{
|
||||
public:
|
||||
enum PinchGestureType
|
||||
{
|
||||
PINCHGESTURE_START,
|
||||
PINCHGESTURE_SCALE,
|
||||
PINCHGESTURE_END
|
||||
};
|
||||
|
||||
PinchGestureInput(PinchGestureType aType,
|
||||
PRUint32 aTime,
|
||||
const nsIntPoint& aFocusPoint,
|
||||
float aCurrentSpan,
|
||||
float aPreviousSpan)
|
||||
: InputData(PINCHGESTURE_INPUT, aTime),
|
||||
mType(aType),
|
||||
mFocusPoint(aFocusPoint),
|
||||
mCurrentSpan(aCurrentSpan),
|
||||
mPreviousSpan(aPreviousSpan)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PinchGestureType mType;
|
||||
|
||||
// Center point of the pinch gesture. That is, if there are two fingers on the
|
||||
// screen, it is their midpoint. In the case of more than two fingers, the
|
||||
// point is implementation-specific, but can for example be the midpoint
|
||||
// between the very first and very last touch. This is in device pixels and
|
||||
// are the coordinates on the screen of this midpoint.
|
||||
nsIntPoint mFocusPoint;
|
||||
|
||||
// The distance in device pixels (though as a float for increased precision
|
||||
// and because it is the distance along both the x and y axis) between the
|
||||
// touches responsible for the pinch gesture.
|
||||
float mCurrentSpan;
|
||||
|
||||
// The previous |mCurrentSpan| in the PinchGestureInput preceding this one.
|
||||
// This is only really relevant during a PINCHGESTURE_SCALE because when it is
|
||||
// of this type then there must have been a history of spans.
|
||||
float mPreviousSpan;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulation class for tap events. In general, these will be generated by
|
||||
* a gesture listener by looking at SingleTouchData/MultiTouchInput instances and
|
||||
* determining whether or not the user was trying to do a gesture.
|
||||
*/
|
||||
class TapGestureInput : public InputData
|
||||
{
|
||||
public:
|
||||
enum TapGestureType
|
||||
{
|
||||
TAPGESTURE_LONG,
|
||||
TAPGESTURE_UP,
|
||||
TAPGESTURE_CONFIRMED,
|
||||
TAPGESTURE_DOUBLE,
|
||||
TAPGESTURE_CANCEL
|
||||
};
|
||||
|
||||
TapGestureInput(TapGestureType aType, PRUint32 aTime, const nsIntPoint& aPoint)
|
||||
: InputData(TAPGESTURE_INPUT, aTime),
|
||||
mType(aType),
|
||||
mPoint(aPoint)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
TapGestureType mType;
|
||||
nsIntPoint mPoint;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // InputData_h__
|
|
@ -62,6 +62,7 @@ EXPORTS_mozilla += \
|
|||
endif
|
||||
|
||||
EXPORTS = \
|
||||
InputData.h \
|
||||
nsIWidget.h \
|
||||
nsGUIEvent.h \
|
||||
nsEvent.h \
|
||||
|
|
Загрузка…
Ссылка в новой задаче