mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
324 lines
10 KiB
C++
324 lines
10 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 sw=2 et tw=78: */
|
|
/* 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_TouchCaret_h__
|
|
#define mozilla_TouchCaret_h__
|
|
|
|
#include "nsISelectionListener.h"
|
|
#include "nsIScrollObserver.h"
|
|
#include "nsIWeakReferenceUtils.h"
|
|
#include "nsITimer.h"
|
|
#include "mozilla/EventForwards.h"
|
|
#include "mozilla/TouchEvents.h"
|
|
#include "Units.h"
|
|
|
|
class nsCanvasFrame;
|
|
class nsIFrame;
|
|
class nsIPresShell;
|
|
|
|
namespace mozilla {
|
|
|
|
/**
|
|
* The TouchCaret places a touch caret according to caret position when the
|
|
* caret is shown.
|
|
* TouchCaret is also responsible for touch caret visibility. Touch caret
|
|
* won't be shown when timer expires or while key event causes selection change.
|
|
*/
|
|
class TouchCaret final : public nsISelectionListener,
|
|
public nsIScrollObserver,
|
|
public nsSupportsWeakReference
|
|
{
|
|
public:
|
|
explicit TouchCaret(nsIPresShell* aPresShell);
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISELECTIONLISTENER
|
|
|
|
void Init();
|
|
void Terminate();
|
|
|
|
// nsIScrollObserver
|
|
virtual void ScrollPositionChanged() override;
|
|
|
|
// AsyncPanZoom started/stopped callbacks from nsIScrollObserver
|
|
virtual void AsyncPanZoomStarted() override;
|
|
virtual void AsyncPanZoomStopped() override;
|
|
|
|
/**
|
|
* Handle mouse and touch event only.
|
|
* Depends on visibility and position of touch caret, HandleEvent may consume
|
|
* that input event and return nsEventStatus_eConsumeNoDefault to the caller.
|
|
* In that case, caller should stop bubble up that input event.
|
|
*/
|
|
nsEventStatus HandleEvent(WidgetEvent* aEvent);
|
|
|
|
void SyncVisibilityWithCaret();
|
|
|
|
void UpdatePositionIfNeeded();
|
|
|
|
/**
|
|
* GetVisibility will get the visibility of the touch caret.
|
|
*/
|
|
bool GetVisibility() const
|
|
{
|
|
return mVisible;
|
|
}
|
|
|
|
/**
|
|
* Open or close the Android TextSelection ActionBar based on visibility.
|
|
*/
|
|
static void UpdateAndroidActionBarVisibility(bool aVisibility, uint32_t& aViewID);
|
|
|
|
private:
|
|
// Hide default constructor.
|
|
TouchCaret() = delete;
|
|
|
|
~TouchCaret();
|
|
|
|
bool IsDisplayable();
|
|
|
|
void UpdatePosition();
|
|
|
|
/**
|
|
* SetVisibility will set the visibility of the touch caret.
|
|
* SetVisibility performs an attribute-changed notification which could, in
|
|
* theory, destroy frames.
|
|
*/
|
|
void SetVisibility(bool aVisible);
|
|
|
|
/**
|
|
* Helper function to get caret's focus frame and caret's bounding rect.
|
|
*/
|
|
nsIFrame* GetCaretFocusFrame(nsRect* aOutRect = nullptr);
|
|
|
|
/**
|
|
* Find the nsCanvasFrame which holds the touch caret.
|
|
*/
|
|
nsCanvasFrame* GetCanvasFrame();
|
|
|
|
/**
|
|
* Find the root frame to update the touch caret's position.
|
|
*/
|
|
nsIFrame* GetRootFrame();
|
|
|
|
/**
|
|
* Retrieve the bounding rectangle of the touch caret.
|
|
*
|
|
* @returns A nsRect representing the bounding rectangle of this touch caret.
|
|
* The returned offset is relative to the canvas frame.
|
|
*/
|
|
nsRect GetTouchFrameRect();
|
|
|
|
/**
|
|
* Retrieve the bounding rectangle where the caret can be positioned.
|
|
* If we're positioning a caret in an input field, make sure the touch caret
|
|
* stays within the bounds of the field.
|
|
*
|
|
* @returns A nsRect representing the bounding rectangle of this valid area.
|
|
* The returned offset is relative to the canvas frame.
|
|
*/
|
|
nsRect GetContentBoundary();
|
|
|
|
/**
|
|
* Retrieve the center y position of the caret.
|
|
* The returned point is relative to the canvas frame.
|
|
*/
|
|
nscoord GetCaretYCenterPosition();
|
|
|
|
/**
|
|
* Retrieve the rect of the touch caret.
|
|
* The returned rect is relative to the canvas frame.
|
|
*/
|
|
nsRect GetTouchCaretRect();
|
|
|
|
/**
|
|
* Clamp the position of the touch caret to the scroll frame boundary.
|
|
* The returned rect is relative to the canvas frame.
|
|
*/
|
|
nsRect ClampRectToScrollFrame(const nsRect& aRect);
|
|
|
|
/**
|
|
* Set the position of the touch caret.
|
|
* Touch caret is an absolute positioned div.
|
|
*/
|
|
void SetTouchFramePos(const nsRect& aRect);
|
|
|
|
void LaunchExpirationTimer();
|
|
void CancelExpirationTimer();
|
|
static void DisableTouchCaretCallback(nsITimer* aTimer, void* aPresShell);
|
|
|
|
/**
|
|
* Move the caret to movePoint which is relative to the canvas frame.
|
|
* Caret will be scrolled into view.
|
|
*
|
|
* @param movePoint tap location relative to the canvas frame.
|
|
*/
|
|
void MoveCaret(const nsPoint& movePoint);
|
|
|
|
/**
|
|
* Check if aPoint is inside the touch caret frame.
|
|
*
|
|
* @param aPoint tap location relative to the canvas frame.
|
|
*/
|
|
bool IsOnTouchCaret(const nsPoint& aPoint);
|
|
|
|
/**
|
|
* These Handle* functions comprise input alphabet of the TouchCaret
|
|
* finite-state machine triggering state transitions.
|
|
*/
|
|
nsEventStatus HandleMouseMoveEvent(WidgetMouseEvent* aEvent);
|
|
nsEventStatus HandleMouseUpEvent(WidgetMouseEvent* aEvent);
|
|
nsEventStatus HandleMouseDownEvent(WidgetMouseEvent* aEvent);
|
|
nsEventStatus HandleTouchMoveEvent(WidgetTouchEvent* aEvent);
|
|
nsEventStatus HandleTouchUpEvent(WidgetTouchEvent* aEvent);
|
|
nsEventStatus HandleTouchDownEvent(WidgetTouchEvent* aEvent);
|
|
|
|
/**
|
|
* Get the coordinates of a given touch event, relative to canvas frame.
|
|
* @param aEvent the event
|
|
* @param aIdentifier the mIdentifier of the touch which is to be converted.
|
|
* @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
|
|
* for some reason the coordinates for the touch are not known (e.g.,
|
|
* the mIdentifier touch is not found).
|
|
*/
|
|
nsPoint GetEventPosition(WidgetTouchEvent* aEvent, int32_t aIdentifier);
|
|
|
|
/**
|
|
* Set mouse down state in nsFrameSelection, we'll set state to true when
|
|
* user start dragging caret and set state to false when user release the
|
|
* caret. The reason for setting this state is it will fire drag reason
|
|
* when moving caret and fire mouseup reason when releasing caret. So that
|
|
* the display behavior of copy/paste menu becomes more reasonable.
|
|
*/
|
|
void SetSelectionDragState(bool aState);
|
|
|
|
/**
|
|
* Get the coordinates of a given mouse event, relative to canvas frame.
|
|
* @param aEvent the event
|
|
* @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
|
|
* for some reason the coordinates for the mouse are not known.
|
|
*/
|
|
nsPoint GetEventPosition(WidgetMouseEvent* aEvent);
|
|
|
|
/**
|
|
* States of TouchCaret finite-state machine.
|
|
*/
|
|
enum TouchCaretState {
|
|
// In this state, either there is no touch/mouse event going on, or the
|
|
// first stroke does not hit the touch caret.
|
|
// Will enter TOUCHCARET_TOUCHDRAG_ACTIVE state if the first touch stroke
|
|
// hits the touch caret. Will enter TOUCHCARET_MOUSEDRAG_ACTIVE state if
|
|
// mouse (left button) down hits the touch caret.
|
|
// Allowed next state: TOUCHCARET_MOUSEDRAG_ACTIVE,
|
|
// TOUCHCARET_TOUCHDRAG_ACTIVE.
|
|
TOUCHCARET_NONE,
|
|
// The first (left button) mouse down hits on the touch caret and is
|
|
// alive. Will enter TOUCHCARET_NONE state if the left button is release.
|
|
// Allowed next states: TOUCHCARET_NONE.
|
|
TOUCHCARET_MOUSEDRAG_ACTIVE,
|
|
// The first touch start event hits on touch caret and is alive.
|
|
// Will enter TOUCHCARET_NONE state if the finger on touch caret is
|
|
// removed and there are no more fingers on the screen; will enter
|
|
// TOUCHCARET_TOUCHDRAG_INACTIVE state if the finger on touch caret is
|
|
// removed but still has fingers touching on the screen.
|
|
// Allowed next states: TOUCHCARET_NONE, TOUCHCARET_TOUCHDRAG_INACTIVE.
|
|
TOUCHCARET_TOUCHDRAG_ACTIVE,
|
|
// The first touch stroke, which hit on touch caret, is dead, but still has
|
|
// fingers touching on the screen.
|
|
// Will enter TOUCHCARET_NONE state if all the fingers are removed from the
|
|
// screen.
|
|
// Allowed next state: TOUCHCARET_NONE.
|
|
TOUCHCARET_TOUCHDRAG_INACTIVE,
|
|
};
|
|
|
|
/**
|
|
* Do actual state transition and reset substates.
|
|
*/
|
|
void SetState(TouchCaretState aState);
|
|
|
|
/**
|
|
* Dispatch touch caret tap event to chrome.
|
|
*/
|
|
void DispatchTapEvent();
|
|
|
|
/**
|
|
* Current state we're dealing with.
|
|
*/
|
|
TouchCaretState mState;
|
|
|
|
/**
|
|
* Array containing all active touch IDs. When a touch happens, it gets added
|
|
* to this array, even if we choose not to handle it. When it ends, we remove
|
|
* it. We need to maintain this array in order to detect the end of the
|
|
* "multitouch" states because touch start events contain all current touches,
|
|
* but touch end events contain only those touches that have gone.
|
|
*/
|
|
nsTArray<int32_t> mTouchesId;
|
|
|
|
/**
|
|
* The mIdentifier of the touch which is on the touch caret.
|
|
*/
|
|
int32_t mActiveTouchId;
|
|
|
|
/**
|
|
* The offset between the tap location and the center of caret along y axis.
|
|
*/
|
|
nscoord mCaretCenterToDownPointOffsetY;
|
|
|
|
/**
|
|
* Get from pref "touchcaret.inflatesize.threshold". This will inflate the
|
|
* size of the touch caret frame when checking if user clicks on the caret
|
|
* or not. In app units.
|
|
*/
|
|
static int32_t TouchCaretInflateSize() { return sTouchCaretInflateSize; }
|
|
|
|
static int32_t TouchCaretExpirationTime()
|
|
{
|
|
return sTouchCaretExpirationTime;
|
|
}
|
|
|
|
void LaunchScrollEndDetector();
|
|
void CancelScrollEndDetector();
|
|
static void FireScrollEnd(nsITimer* aTimer, void* aSelectionCarets);
|
|
|
|
// This timer is used for detecting scroll end. We don't have
|
|
// scroll end event now, so we will fire this event with a
|
|
// const time when we scroll. So when timer triggers, we treat it
|
|
// as scroll end event.
|
|
nsCOMPtr<nsITimer> mScrollEndDetectorTimer;
|
|
|
|
nsWeakPtr mPresShell;
|
|
WeakPtr<nsDocShell> mDocShell;
|
|
|
|
// True if AsyncPanZoom is started
|
|
bool mInAsyncPanZoomGesture;
|
|
|
|
// Touch caret visibility
|
|
bool mVisible;
|
|
// Use for detecting single tap on touch caret.
|
|
bool mIsValidTap;
|
|
// Touch caret timer
|
|
nsCOMPtr<nsITimer> mTouchCaretExpirationTimer;
|
|
|
|
// Preference
|
|
static int32_t sTouchCaretInflateSize;
|
|
static int32_t sTouchCaretExpirationTime;
|
|
static bool sCaretManagesAndroidActionbar;
|
|
static bool sTouchcaretExtendedvisibility;
|
|
|
|
// The auto scroll timer's interval in miliseconds.
|
|
friend class SelectionCarets;
|
|
static const int32_t sAutoScrollTimerDelay = 30;
|
|
// Time for trigger scroll end event, in miliseconds.
|
|
static const int32_t sScrollEndTimerDelay = 300;
|
|
|
|
// Unique ID of current Mobile ActionBar view.
|
|
static uint32_t sActionBarViewCount;
|
|
uint32_t mActionBarViewID;
|
|
};
|
|
} //namespace mozilla
|
|
#endif //mozilla_TouchCaret_h__
|