mirror of
https://github.com/azahar-emu/azahar
synced 2025-11-06 15:09:58 +01:00
android: Added button sliding to button overlay (#884)
* Added simple button sliding mode * Added "keep first" button sliding mode * directly pressed buttons stay active even when sliding off * further buttons can be triggered via the simple sliding method * Added button sliding configuration to overlay settings menu * Updated licences * Added button sliding activation to dpads and joysticks * separated handling of buttons, dpads and joysticks needed since they can be activated by moving now * Adjusted strings * Changed ButtonSlidingMode values to mirror prior string name changes * Reverted incorrectly applied language translation * Nitpicky formatting adjustments * Shortened string IDs * hasActiveJoy --> hasActiveJoystick * showButtonSlidingModeMenu --> showButtonSlidingMenu * Updated outdated comment relating to `isMotionFirstButton` Co-authored-by: toksn <toksn@yahoo.de> --------- Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
This commit is contained in:
parent
00c0f01e73
commit
c95b942ec2
@ -796,6 +796,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_emulation_button_sliding -> {
|
||||
showButtonSlidingMenu()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_emulation_dpad_slide_enable -> {
|
||||
EmulationMenuSettings.dpadSlide = !EmulationMenuSettings.dpadSlide
|
||||
true
|
||||
@ -840,6 +845,28 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
popupMenu.show()
|
||||
}
|
||||
|
||||
private fun showButtonSlidingMenu() {
|
||||
val editor = preferences.edit()
|
||||
|
||||
val buttonSlidingModes = mutableListOf<String>()
|
||||
buttonSlidingModes.add(getString(R.string.emulation_button_sliding_disabled))
|
||||
buttonSlidingModes.add(getString(R.string.emulation_button_sliding_enabled))
|
||||
buttonSlidingModes.add(getString(R.string.emulation_button_sliding_alternative))
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.emulation_button_sliding)
|
||||
.setSingleChoiceItems(
|
||||
buttonSlidingModes.toTypedArray(),
|
||||
EmulationMenuSettings.buttonSlide
|
||||
) { _: DialogInterface?, which: Int ->
|
||||
EmulationMenuSettings.buttonSlide = which
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||
editor.apply()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showLandscapeScreenLayoutMenu() {
|
||||
val popupMenu = PopupMenu(
|
||||
requireContext(),
|
||||
|
||||
@ -96,57 +96,115 @@ 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 hasActiveJoystick = false
|
||||
if(!hasActiveButtons && !hasActiveDpad){
|
||||
for (joystick in overlayJoysticks) {
|
||||
if (joystick.trackId == pointerId) {
|
||||
hasActiveJoystick = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var shouldUpdateView = false
|
||||
if(!hasActiveDpad && !hasActiveJoystick) {
|
||||
for (button in overlayButtons) {
|
||||
val stateChanged = button.updateStatus(event, hasActiveButtons, this)
|
||||
if (!stateChanged) {
|
||||
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 && !hasActiveJoystick) {
|
||||
for (dpad in overlayDpads) {
|
||||
val stateChanged = dpad.updateStatus(
|
||||
event,
|
||||
hasActiveDpad,
|
||||
EmulationMenuSettings.dpadSlide,
|
||||
this
|
||||
)
|
||||
if (!stateChanged) {
|
||||
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) {
|
||||
val stateChanged = joystick.updateStatus(event, hasActiveJoystick, this)
|
||||
if (!stateChanged) {
|
||||
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 +215,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
|
||||
|
||||
@ -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,20 @@ 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
|
||||
|
||||
enum class ButtonSlidingMode(val int: Int) {
|
||||
// Disabled, buttons can only be triggered by pressing them directly.
|
||||
Disabled(0),
|
||||
|
||||
// Additionally to pressing buttons directly, they can be activated and released by sliding into
|
||||
// and out of their area.
|
||||
Enabled(1),
|
||||
|
||||
// The first button is kept activated until released, further buttons use the simple button
|
||||
// sliding method.
|
||||
Alternative(2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom [BitmapDrawable] that is capable
|
||||
@ -30,6 +44,9 @@ class InputOverlayDrawableButton(
|
||||
val opacity: Int
|
||||
) {
|
||||
var trackId: Int
|
||||
|
||||
private var isMotionFirstButton = false // mark the first activated button with the current motion
|
||||
|
||||
private var previousTouchX = 0
|
||||
private var previousTouchY = 0
|
||||
private var controlPositionX = 0
|
||||
@ -53,7 +70,8 @@ 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()
|
||||
val yPosition = event.getY(pointerIndex).toInt()
|
||||
@ -67,23 +85,60 @@ class InputOverlayDrawableButton(
|
||||
if (!bounds.contains(xPosition, yPosition)) {
|
||||
return false
|
||||
}
|
||||
pressedState = true
|
||||
trackId = pointerId
|
||||
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
||||
buttonDown(true, pointerId, overlay)
|
||||
return true
|
||||
}
|
||||
if (isActionUp) {
|
||||
if (trackId != pointerId) {
|
||||
return false
|
||||
}
|
||||
pressedState = false
|
||||
trackId = -1
|
||||
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
|
||||
buttonUp(overlay)
|
||||
return true
|
||||
}
|
||||
|
||||
val isActionMoving = motionEvent == MotionEvent.ACTION_MOVE
|
||||
if (buttonSliding != ButtonSlidingMode.Disabled.int && isActionMoving) {
|
||||
val inside = bounds.contains(xPosition, yPosition)
|
||||
if (pressedState) {
|
||||
// button is already pressed
|
||||
// check whether we moved out of the button area to update the state
|
||||
if (inside || trackId != pointerId) {
|
||||
return false
|
||||
}
|
||||
// prevent the first (directly pressed) button to deactivate when sliding off
|
||||
if (buttonSliding == ButtonSlidingMode.Alternative.int && isMotionFirstButton) {
|
||||
return false
|
||||
}
|
||||
buttonUp(overlay)
|
||||
return true
|
||||
} else {
|
||||
// button was not yet pressed
|
||||
// check whether we moved into the button area to update the state
|
||||
if (!inside) {
|
||||
return false
|
||||
}
|
||||
buttonDown(!hasActiveButtons, pointerId, overlay)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@ -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.Disabled.int) {
|
||||
isActionDown = motionEvent == MotionEvent.ACTION_MOVE && !hasActiveButtons
|
||||
}
|
||||
|
||||
val isActionUp =
|
||||
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
|
||||
if (isActionDown) {
|
||||
|
||||
@ -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.Disabled.int) {
|
||||
isActionDown = motionEvent == MotionEvent.ACTION_MOVE && !hasActiveButtons
|
||||
}
|
||||
|
||||
val isActionUp =
|
||||
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
|
||||
if (isActionDown) {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -7,6 +7,7 @@ package org.citra.citra_emu.utils
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.overlay.ButtonSlidingMode
|
||||
|
||||
object EmulationMenuSettings {
|
||||
private val preferences =
|
||||
@ -26,6 +27,13 @@ object EmulationMenuSettings {
|
||||
.putBoolean("EmulationMenuSettings_DpadSlideEnable", value)
|
||||
.apply()
|
||||
}
|
||||
var buttonSlide: Int
|
||||
get() = preferences.getInt("EmulationMenuSettings_ButtonSlideMode", ButtonSlidingMode.Disabled.int)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putInt("EmulationMenuSettings_ButtonSlideMode", value)
|
||||
.apply()
|
||||
}
|
||||
|
||||
var showPerformanceOverlay: Boolean
|
||||
get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
|
||||
|
||||
@ -86,6 +86,10 @@
|
||||
android:id="@+id/menu_emulation_adjust_opacity"
|
||||
android:title="@string/emulation_control_opacity" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_emulation_button_sliding"
|
||||
android:title="@string/emulation_button_sliding">
|
||||
</item>
|
||||
<group android:checkableBehavior="all">
|
||||
<item
|
||||
android:id="@+id/menu_emulation_joystick_rel_center"
|
||||
|
||||
@ -421,6 +421,10 @@
|
||||
<string name="emulation_configure_controls">Configure Controls</string>
|
||||
<string name="emulation_edit_layout">Edit Layout</string>
|
||||
<string name="emulation_done">Done</string>
|
||||
<string name="emulation_button_sliding">Button Sliding</string>
|
||||
<string name="emulation_button_sliding_disabled">Hold originally pressed button</string>
|
||||
<string name="emulation_button_sliding_enabled">Hold currently pressed button</string>
|
||||
<string name="emulation_button_sliding_alternative">Hold original and currently pressed button</string>
|
||||
<string name="emulation_toggle_controls">Toggle Controls</string>
|
||||
<string name="emulation_control_scale">Adjust Scale</string>
|
||||
<string name="emulation_control_scale_global">Global Scale</string>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user