public override void Update(GameTime gameTime) { bool checkForKeyPress; #if NETCOREAPP MouseState mouseState = XnaMouse.GetState(); checkForKeyPress = prevMouseState.LeftButton == XnaButtonState.Pressed && mouseState.LeftButton == XnaButtonState.Released; #endif #if ANDROID TouchCollection touchCollection = XnaTouchPanel.GetState(); checkForKeyPress = touchCollection.Count > 0 && touchCollection[0].State == TouchLocationState.Released; // TODO multi-touch support? Vector2 touchPos = touchCollection.Count > 0 ? touchCollection[0].Position : Vector2.Zero; #endif foreach (VirtualKey key in keys) { if (checkForKeyPress) { #if WINDOWS if (mouseState.Y > Y + key.Y && mouseState.Y < Y + key.Y + key.Height && mouseState.X > X + key.X && mouseState.X < X + key.X + key.Width) { HandleKeyPress(key); checkForKeyPress = false; } #endif #if ANDROID if (touchPos.Y > Y + key.Y && touchPos.Y < Y + key.Y + key.Height && touchPos.X > X + key.X && touchPos.X < X + key.X + key.Width) { HandleKeyPress(key); checkForKeyPress = false; } #endif } key.Update(gameTime); } #if WINDOWS prevMouseState = mouseState; #endif base.Update(gameTime); }
internal static void OnReleased(int fingerId, Vector2 touchPosition) { fingerIds.Remove(fingerId); // Handle release events seperately for Pinch gestures if (state == GestureState.PINCHING) { OnReleased_Pinch(fingerId, touchPosition); return; } // Did the user lift the active finger? if (fingerId == activeFingerId) { activeFingerId = TouchPanel.NO_FINGER; } // We're only interested in the very last finger to leave if (FNAPlatform.GetNumTouchFingers() > 0) { return; } #region Tap Detection if (state == GestureState.HOLDING) { // Which Tap gestures are enabled? bool tapEnabled = IsGestureEnabled(GestureType.Tap); bool dtapEnabled = IsGestureEnabled(GestureType.DoubleTap); if (tapEnabled || dtapEnabled) { // How long did the user hold the touch? TimeSpan timeHeld = DateTime.Now - eventTimestamp; if (timeHeld < TimeSpan.FromSeconds(1)) { // Don't register a Tap immediately after a Double Tap if (!justDoubleTapped) { if (tapEnabled) { // Tap! TouchPanel.EnqueueGesture(new GestureSample( GestureType.Tap, GetGestureTimestamp(), touchPosition, Vector2.Zero, Vector2.Zero, Vector2.Zero, fingerId, TouchPanel.NO_FINGER )); } /* Even if Tap isn't enabled, we still * need this for Double Tap detection. */ state = GestureState.JUST_TAPPED; } } } } // Reset this flag so we can catch Taps in the future justDoubleTapped = false; #endregion #region Flick Detection if (IsGestureEnabled(GestureType.Flick)) { // Only flick if the finger is outside the threshold and moving fast float distanceFromPress = (touchPosition - pressPosition).Length(); if (distanceFromPress > MOVE_THRESHOLD && velocity.Length() >= MIN_FLICK_VELOCITY) { // Flick! TouchPanel.EnqueueGesture(new GestureSample( GestureType.Flick, GetGestureTimestamp(), Vector2.Zero, Vector2.Zero, velocity, Vector2.Zero, fingerId, TouchPanel.NO_FINGER )); } // Reset velocity calculation variables velocity = Vector2.Zero; lastUpdatePosition = Vector2.Zero; updateTimestamp = DateTime.MinValue; } #endregion #region Drag Complete Detection if (IsGestureEnabled(GestureType.DragComplete)) { bool wasDragging = (state == GestureState.DRAGGING_H || state == GestureState.DRAGGING_V || state == GestureState.DRAGGING_FREE); if (wasDragging) { // Drag Complete! TouchPanel.EnqueueGesture(new GestureSample( GestureType.DragComplete, GetGestureTimestamp(), Vector2.Zero, Vector2.Zero, Vector2.Zero, Vector2.Zero, fingerId, TouchPanel.NO_FINGER )); } } #endregion #region Belated Pinch Complete Detection if (callBelatedPinchComplete && IsGestureEnabled(GestureType.PinchComplete)) { TouchPanel.EnqueueGesture(new GestureSample( GestureType.PinchComplete, GetGestureTimestamp(), Vector2.Zero, Vector2.Zero, Vector2.Zero, Vector2.Zero, TouchPanel.NO_FINGER, TouchPanel.NO_FINGER )); } callBelatedPinchComplete = false; #endregion // Reset the state if we're not anticipating a Double Tap if (state != GestureState.JUST_TAPPED) { state = GestureState.NONE; } eventTimestamp = DateTime.Now; }
internal static void OnPressed(int fingerId, Vector2 touchPosition) { fingerIds.Add(fingerId); if (state == GestureState.PINCHING) { // None of this method applies to active pinches return; } // Set the active finger if there isn't one already if (activeFingerId == TouchPanel.NO_FINGER) { activeFingerId = fingerId; activeFingerPosition = touchPosition; } else { #region Pinch Initialization if (IsGestureEnabled(GestureType.Pinch)) { // Initialize a Pinch secondFingerId = fingerId; secondFingerPosition = touchPosition; state = GestureState.PINCHING; } #endregion // No need to do anything more return; } #region Double Tap Detection if (state == GestureState.JUST_TAPPED) { if (IsGestureEnabled(GestureType.DoubleTap)) { // Must tap again within 300ms of original tap's release TimeSpan timeSinceRelease = DateTime.Now - eventTimestamp; if (timeSinceRelease <= TimeSpan.FromMilliseconds(300)) { // If the new tap is close to the original tap float distance = (touchPosition - pressPosition).Length(); if (distance <= MOVE_THRESHOLD) { // Double Tap! TouchPanel.EnqueueGesture(new GestureSample( GestureType.DoubleTap, GetGestureTimestamp(), touchPosition, Vector2.Zero, Vector2.Zero, Vector2.Zero, fingerId, TouchPanel.NO_FINGER )); justDoubleTapped = true; } } } } #endregion state = GestureState.HOLDING; pressPosition = touchPosition; eventTimestamp = DateTime.Now; }
internal static void OnUpdate() { if (state == GestureState.PINCHING) { /* Handle the case where the Pinch gesture * was disabled *while* the user was pinching. */ if (!IsGestureEnabled(GestureType.Pinch)) { state = GestureState.HELD; secondFingerId = TouchPanel.NO_FINGER; // Still might need to trigger a PinchComplete callBelatedPinchComplete = true; } // No pinches allowed in the rest of this method! return; } // Must have an active finger to proceed if (activeFingerId == TouchPanel.NO_FINGER) { return; } #region Flick Velocity Calculation if (IsGestureEnabled(GestureType.Flick)) { // We need one frame to pass so we can calculate delta time if (updateTimestamp != DateTime.MinValue) { /* The calculation below is mostly taken from MonoGame. * It accumulates velocity after running it through * a low-pass filter to mitigate the effect of * acceleration spikes. This works pretty well, * but on rare occasions the velocity will still * spike by an order of magnitude. * * In practice this tends to be a non-issue, but * if you *really* need to avoid any spikes, you * may want to consider normalizing the delta * reported in the GestureSample and then scaling it * to min(actualVectorLength, preferredMaxLength). * * -caleb */ float dt = (float)(DateTime.Now - updateTimestamp).TotalSeconds; Vector2 delta = activeFingerPosition - lastUpdatePosition; Vector2 instVelocity = delta / (0.001f + dt); velocity += (instVelocity - velocity) * 0.45f; } lastUpdatePosition = activeFingerPosition; updateTimestamp = DateTime.Now; } #endregion #region Hold Detection if (IsGestureEnabled(GestureType.Hold) && state == GestureState.HOLDING) { TimeSpan timeSincePress = DateTime.Now - eventTimestamp; if (timeSincePress >= TimeSpan.FromSeconds(1)) { // Hold! TouchPanel.EnqueueGesture(new GestureSample( Vector2.Zero, Vector2.Zero, GestureType.Hold, activeFingerPosition, Vector2.Zero, GetGestureTimestamp(), activeFingerId, TouchPanel.NO_FINGER )); state = GestureState.HELD; } } #endregion }
internal static void OnMoved(int fingerId, Vector2 touchPosition, Vector2 delta) { // Handle move events separately for Pinch gestures if (state == GestureState.PINCHING) { OnMoved_Pinch(fingerId, touchPosition, delta); return; } // Replace the active finger if we lost it if (activeFingerId == TouchPanel.NO_FINGER) { activeFingerId = fingerId; } // If this finger isn't the active finger if (fingerId != activeFingerId) { // We don't care about it return; } // Update the position activeFingerPosition = touchPosition; #region Prepare for Dragging // Determine which drag gestures are enabled bool hdrag = IsGestureEnabled(GestureType.HorizontalDrag); bool vdrag = IsGestureEnabled(GestureType.VerticalDrag); bool fdrag = IsGestureEnabled(GestureType.FreeDrag); if (state == GestureState.HOLDING || state == GestureState.HELD) { // Prevent accidental drags float distanceFromPress = (touchPosition - pressPosition).Length(); if (distanceFromPress > MOVE_THRESHOLD) { if (hdrag && (Math.Abs(delta.X) > Math.Abs(delta.Y))) { // Horizontal Drag! state = GestureState.DRAGGING_H; } else if (vdrag && (Math.Abs(delta.Y) > Math.Abs(delta.X))) { // Vertical Drag! state = GestureState.DRAGGING_V; } else if (fdrag) { // Free Drag! state = GestureState.DRAGGING_FREE; } else { // No drag... state = GestureState.NONE; } } } #endregion #region Drag Detection if (state == GestureState.DRAGGING_H && hdrag) { // Horizontal Dragging! TouchPanel.EnqueueGesture(new GestureSample( new Vector2(delta.X, 0), Vector2.Zero, GestureType.HorizontalDrag, touchPosition, Vector2.Zero, GetGestureTimestamp(), fingerId, TouchPanel.NO_FINGER )); } else if (state == GestureState.DRAGGING_V && vdrag) { // Vertical Dragging! TouchPanel.EnqueueGesture(new GestureSample( new Vector2(0, delta.Y), Vector2.Zero, GestureType.VerticalDrag, touchPosition, Vector2.Zero, GetGestureTimestamp(), fingerId, TouchPanel.NO_FINGER )); } else if (state == GestureState.DRAGGING_FREE && fdrag) { // Free Dragging! TouchPanel.EnqueueGesture(new GestureSample( delta, Vector2.Zero, GestureType.FreeDrag, touchPosition, Vector2.Zero, GetGestureTimestamp(), fingerId, TouchPanel.NO_FINGER )); } #endregion #region Handle Disabled Drags /* Handle the case where the current drag type * was disabled *while* the user was dragging. */ if ((state == GestureState.DRAGGING_H && !hdrag) || (state == GestureState.DRAGGING_V && !vdrag) || (state == GestureState.DRAGGING_FREE && !fdrag)) { // Reset the state state = GestureState.HELD; } #endregion }
private static void UpdateGestures(bool stateChanged) { int num1 = 0; foreach (TouchLocation touchLocation in TouchPanel._gestureState) { num1 += touchLocation.State != TouchLocationState.Released ? 1 : 0; } if (num1 > 1) { TouchPanel._tapDisabled = true; TouchPanel._holdDisabled = true; } foreach (TouchLocation touch in TouchPanel._gestureState) { switch (touch.State) { case TouchLocationState.Moved: case TouchLocationState.Pressed: if (touch.State != TouchLocationState.Pressed || !TouchPanel.ProcessDoubleTap(touch)) { if (TouchPanel.GestureIsEnabled(GestureType.Pinch) && num1 > 1) { if (TouchPanel._pinchTouch[0].State == TouchLocationState.Invalid || TouchPanel._pinchTouch[0].Id == touch.Id) { TouchPanel._pinchTouch[0] = touch; continue; } else if (TouchPanel._pinchTouch[1].State == TouchLocationState.Invalid || TouchPanel._pinchTouch[1].Id == touch.Id) { TouchPanel._pinchTouch[1] = touch; continue; } else { continue; } } else { float num2 = Vector2.Distance(touch.Position, touch.PressPosition); if (TouchPanel._dragGestureStarted == GestureType.None && (double)num2 < 35.0) { TouchPanel.ProcessHold(touch); continue; } else if (stateChanged) { TouchPanel.ProcessDrag(touch); continue; } else { continue; } } } else { continue; } case TouchLocationState.Released: if (stateChanged) { if (TouchPanel._pinchGestureStarted && (touch.Id == TouchPanel._pinchTouch[0].Id || touch.Id == TouchPanel._pinchTouch[1].Id)) { if (TouchPanel.GestureIsEnabled(GestureType.PinchComplete)) { TouchPanel.GestureList.Enqueue(new GestureSample(GestureType.PinchComplete, touch.Timestamp, Vector2.Zero, Vector2.Zero, Vector2.Zero, Vector2.Zero)); } TouchPanel._pinchGestureStarted = false; TouchPanel._pinchTouch[0] = TouchLocation.Invalid; TouchPanel._pinchTouch[1] = TouchLocation.Invalid; continue; } else if (num1 == 0) { if ((double)Vector2.Distance(touch.Position, touch.PressPosition) > 35.0 && ((double)touch.Velocity.Length() > 100.0 && TouchPanel.GestureIsEnabled(GestureType.Flick))) { TouchPanel.GestureList.Enqueue(new GestureSample(GestureType.Flick, touch.Timestamp, Vector2.Zero, Vector2.Zero, touch.Velocity, Vector2.Zero)); TouchPanel._dragGestureStarted = GestureType.None; continue; } else if (TouchPanel._dragGestureStarted != GestureType.None) { if (TouchPanel.GestureIsEnabled(GestureType.DragComplete)) { TouchPanel.GestureList.Enqueue(new GestureSample(GestureType.DragComplete, touch.Timestamp, Vector2.Zero, Vector2.Zero, Vector2.Zero, Vector2.Zero)); } TouchPanel._dragGestureStarted = GestureType.None; continue; } else { TouchPanel.ProcessTap(touch); continue; } } else { continue; } } else { continue; } default: continue; } } if (!stateChanged) { return; } if (TouchPanel.GestureIsEnabled(GestureType.Pinch) && TouchPanel._pinchTouch[0].State != TouchLocationState.Invalid && TouchPanel._pinchTouch[1].State != TouchLocationState.Invalid) { TouchPanel.ProcessPinch(TouchPanel._pinchTouch); } else { TouchPanel._pinchGestureStarted = false; TouchPanel._pinchTouch[0] = TouchLocation.Invalid; TouchPanel._pinchTouch[1] = TouchLocation.Invalid; } if (num1 != 0) { return; } TouchPanel._tapDisabled = false; TouchPanel._holdDisabled = false; TouchPanel._dragGestureStarted = GestureType.None; }
internal static void AddEvent(int id, TouchLocationState state, Vector2 position) { TouchPanel.AddEvent(id, state, position, false); }