Added button sliding activation to dpads and joysticks

* separated handling of buttons, dpads and joysticks needed since they can be activated by moving now
This commit is contained in:
toksn 2025-04-08 23:09:28 +02:00 committed by OpenSauce04
parent 55fc3b2aa3
commit 2e06c8ace1
4 changed files with 133 additions and 72 deletions

View file

@ -96,57 +96,108 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
if (isInEditMode) {
return onTouchWhileEditing(event)
}
var shouldUpdateView = false
var hasActiveButtons = false
val pointerIndex = event.actionIndex
val pointerId = event.getPointerId(pointerIndex)
for (button in overlayButtons) {
if (!button.updateStatus(event, this)) {
continue
if (button.trackId == pointerId) {
hasActiveButtons = true
break
}
if (button.id == NativeLibrary.ButtonType.BUTTON_SWAP && button.status == NativeLibrary.ButtonState.PRESSED) {
swapScreen()
}
if (button.id == NativeLibrary.ButtonType.BUTTON_TURBO && button.status == NativeLibrary.ButtonState.PRESSED) {
TurboHelper.toggleTurbo(true)
}
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.id, button.status)
shouldUpdateView = true
}
for (dpad in overlayDpads) {
if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide, this)) {
continue
var hasActiveDpad = false
if (!hasActiveButtons) {
for (dpad in overlayDpads) {
if (dpad.trackId == pointerId) {
hasActiveDpad = true
break
}
}
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.upId, dpad.upStatus)
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.downId,
dpad.downStatus
)
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.leftId,
dpad.leftStatus
)
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.rightId,
dpad.rightStatus
)
shouldUpdateView = true
}
for (joystick in overlayJoysticks) {
if (!joystick.updateStatus(event, this)) {
continue
var hasActiveJoy = false
if(!hasActiveButtons && !hasActiveDpad){
for (joystick in overlayJoysticks) {
if (joystick.trackId == pointerId) {
hasActiveJoy = true
break
}
}
}
var shouldUpdateView = false
if(!hasActiveDpad && !hasActiveJoy) {
for (button in overlayButtons) {
if (!button.updateStatus(event, hasActiveButtons, this)) {
continue
}
if (button.id == NativeLibrary.ButtonType.BUTTON_SWAP && button.status == NativeLibrary.ButtonState.PRESSED) {
swapScreen()
}
else if (button.id == NativeLibrary.ButtonType.BUTTON_TURBO && button.status == NativeLibrary.ButtonState.PRESSED) {
TurboHelper.toggleTurbo(true)
}
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
button.id,
button.status
)
shouldUpdateView = true
}
}
if(!hasActiveButtons && !hasActiveJoy) {
for (dpad in overlayDpads) {
if (!dpad.updateStatus(
event,
hasActiveDpad,
EmulationMenuSettings.dpadSlide,
this
)
) {
continue
}
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.upId,
dpad.upStatus
)
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.downId,
dpad.downStatus
)
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.leftId,
dpad.leftStatus
)
NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice,
dpad.rightId,
dpad.rightStatus
)
shouldUpdateView = true
}
}
if(!hasActiveDpad && !hasActiveButtons) {
for (joystick in overlayJoysticks) {
if (!joystick.updateStatus(event, hasActiveJoy, this)) {
continue
}
val axisID = joystick.joystickId
NativeLibrary.onGamePadMoveEvent(
NativeLibrary.TouchScreenDevice,
axisID,
joystick.xAxis,
joystick.yAxis
)
shouldUpdateView = true
}
val axisID = joystick.joystickId
NativeLibrary.onGamePadMoveEvent(
NativeLibrary.TouchScreenDevice,
axisID,
joystick.xAxis,
joystick.yAxis
)
shouldUpdateView = true
}
if (shouldUpdateView) {
@ -157,10 +208,8 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
return true
}
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex)
val motionEvent = event.action and MotionEvent.ACTION_MASK
val isActionDown =
motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN

View file

@ -22,7 +22,7 @@ enum class ButtonSlidingMode(val int: Int) {
// and out of their area.
Simple(1),
// A pressed button is kept activated until released. Further buttons can be triggered with the
// The first button is kept activated until released, further buttons use the simple button
// sliding method.
KeepFirst(2)
}
@ -45,7 +45,7 @@ class InputOverlayDrawableButton(
) {
var trackId: Int
private var pressedDirectTouch = false // mark buttons that did not get activated by sliding
private var isMotionFirstButton = false // mark buttons that did not get activated by sliding
private var previousTouchX = 0
private var previousTouchY = 0
@ -70,7 +70,7 @@ class InputOverlayDrawableButton(
*
* @return true if value was changed
*/
fun updateStatus(event: MotionEvent, overlay:InputOverlay): Boolean {
fun updateStatus(event: MotionEvent, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean {
val buttonSliding = EmulationMenuSettings.buttonSlide
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
@ -85,20 +85,14 @@ class InputOverlayDrawableButton(
if (!bounds.contains(xPosition, yPosition)) {
return false
}
pressedState = true
pressedDirectTouch = true
trackId = pointerId
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
buttonDown(true, pointerId, overlay)
return true
}
if (isActionUp) {
if (trackId != pointerId) {
return false
}
pressedState = false
pressedDirectTouch = false
trackId = -1
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
buttonUp(overlay)
return true
}
@ -112,13 +106,10 @@ class InputOverlayDrawableButton(
return false
}
// prevent the first (directly pressed) button to deactivate when sliding off
if (buttonSliding == ButtonSlidingMode.KeepFirst.int && pressedDirectTouch) {
if (buttonSliding == ButtonSlidingMode.KeepFirst.int && isMotionFirstButton) {
return false
}
pressedState = false
trackId = -1
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
buttonUp(overlay)
return true
} else {
// button was not yet pressed
@ -126,9 +117,7 @@ class InputOverlayDrawableButton(
if (!inside) {
return false
}
pressedState = true
trackId = pointerId
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
buttonDown(!hasActiveButtons, pointerId, overlay)
return true
}
}
@ -136,6 +125,20 @@ class InputOverlayDrawableButton(
return false
}
private fun buttonDown(firstBtn: Boolean, pointerId: Int, overlay: InputOverlay) {
pressedState = true
isMotionFirstButton = firstBtn
trackId = pointerId
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
}
private fun buttonUp(overlay: InputOverlay) {
pressedState = false
isMotionFirstButton = false
trackId = -1
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
}
fun onConfigureTouch(event: MotionEvent): Boolean {
val pointerIndex = event.actionIndex
val fingerPositionX = event.getX(pointerIndex).toInt()

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -12,6 +12,7 @@ import android.graphics.drawable.BitmapDrawable
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.utils.EmulationMenuSettings
/**
* Custom [BitmapDrawable] that is capable
@ -62,15 +63,19 @@ class InputOverlayDrawableDpad(
trackId = -1
}
fun updateStatus(event: MotionEvent, dpadSlide: Boolean, overlay:InputOverlay): Boolean {
fun updateStatus(event: MotionEvent, hasActiveButtons: Boolean, dpadSlide: Boolean, overlay: InputOverlay): Boolean {
var isDown = false
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex)
val motionEvent = event.action and MotionEvent.ACTION_MASK
val isActionDown =
var isActionDown =
motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN
if (!isActionDown && EmulationMenuSettings.buttonSlide != ButtonSlidingMode.None.int) {
isActionDown = motionEvent == MotionEvent.ACTION_MOVE && !hasActiveButtons
}
val isActionUp =
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
if (isActionDown) {

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -93,14 +93,18 @@ class InputOverlayDrawableJoystick(
currentStateBitmapDrawable.draw(canvas)
}
fun updateStatus(event: MotionEvent, overlay:InputOverlay): Boolean {
fun updateStatus(event: MotionEvent, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean {
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex)
val motionEvent = event.action and MotionEvent.ACTION_MASK
val isActionDown =
var isActionDown =
motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN
if (!isActionDown && EmulationMenuSettings.buttonSlide != ButtonSlidingMode.None.int) {
isActionDown = motionEvent == MotionEvent.ACTION_MOVE && !hasActiveButtons
}
val isActionUp =
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
if (isActionDown) {