mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-27 17:35:28 +03:00
Rework OSC edit mode handling for better extensibility
Edit mode configuration parameters are now shared between the view and the buttons in a small `OnScreenEditInfo` object, avoiding variable duplication about edit state. The `editingTouchHandler` has also been simplified to only lookup the button if one wasn't being edited already.
This commit is contained in:
parent
c4d0d02509
commit
81eb8fd231
@ -82,8 +82,10 @@ abstract class OnScreenButton(
|
|||||||
|
|
||||||
var hapticFeedback = false
|
var hapticFeedback = false
|
||||||
|
|
||||||
var isEditing = false
|
/**
|
||||||
private set
|
* The edit session information, populated by the view
|
||||||
|
*/
|
||||||
|
protected var editInfo = onScreenControllerView.editInfo
|
||||||
|
|
||||||
protected open fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float, alpha : Int) {
|
protected open fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float, alpha : Int) {
|
||||||
buttonSymbolPaint.apply {
|
buttonSymbolPaint.apply {
|
||||||
@ -133,19 +135,35 @@ abstract class OnScreenButton(
|
|||||||
relativeY = config.relativeY
|
relativeY = config.relativeY
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startEdit() {
|
/**
|
||||||
isEditing = true
|
* Starts an edit session
|
||||||
|
* @param x The x coordinate of the initial touch
|
||||||
|
* @param y The y coordinate of the initial touch
|
||||||
|
*/
|
||||||
|
open fun startEdit(x : Float, y : Float) {
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun edit(x : Float, y : Float) {
|
open fun edit(x : Float, y : Float) {
|
||||||
|
when (editInfo.editMode) {
|
||||||
|
EditMode.Move -> move(x, y)
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves this button to the given coordinates
|
||||||
|
*/
|
||||||
|
open fun move(x : Float, y : Float) {
|
||||||
relativeX = x / width
|
relativeX = x / width
|
||||||
relativeY = (y - heightDiff) / adjustedHeight
|
relativeY = (y - heightDiff) / adjustedHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endEdit() {
|
/**
|
||||||
|
* Ends the current edit session
|
||||||
|
*/
|
||||||
|
open fun endEdit() {
|
||||||
config.relativeX = relativeX
|
config.relativeX = relativeX
|
||||||
config.relativeY = relativeY
|
config.relativeY = relativeY
|
||||||
isEditing = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun resetRelativeValues() {
|
open fun resetRelativeValues() {
|
||||||
|
@ -43,7 +43,6 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
|||||||
private val ALPHA_RANGE = 55..255
|
private val ALPHA_RANGE = 55..255
|
||||||
}
|
}
|
||||||
|
|
||||||
private val controls = Controls(this)
|
|
||||||
private var onButtonStateChangedListener : OnButtonStateChangedListener? = null
|
private var onButtonStateChangedListener : OnButtonStateChangedListener? = null
|
||||||
private var onStickStateChangedListener : OnStickStateChangedListener? = null
|
private var onStickStateChangedListener : OnStickStateChangedListener? = null
|
||||||
private val joystickAnimators = mutableMapOf<JoystickButton, Animator?>()
|
private val joystickAnimators = mutableMapOf<JoystickButton, Animator?>()
|
||||||
@ -63,10 +62,15 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
|||||||
(controls.circularButtons + controls.rectangularButtons + controls.triggerButtons).forEach { it.hapticFeedback = hapticFeedback }
|
(controls.circularButtons + controls.rectangularButtons + controls.triggerButtons).forEach { it.hapticFeedback = hapticFeedback }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val editInfo = OnScreenEditInfo()
|
||||||
|
val isEditing get() = editInfo.isEditing
|
||||||
|
|
||||||
// Populated externally by the activity, as retrieving the vibrator service inside the view crashes the layout editor
|
// Populated externally by the activity, as retrieving the vibrator service inside the view crashes the layout editor
|
||||||
lateinit var vibrator : Vibrator
|
lateinit var vibrator : Vibrator
|
||||||
private val effectClick = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
|
private val effectClick = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
|
||||||
|
|
||||||
|
private val controls = Controls(this)
|
||||||
|
|
||||||
override fun onDraw(canvas : Canvas) {
|
override fun onDraw(canvas : Canvas) {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
@ -218,42 +222,50 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val editingTouchHandler = OnTouchListener { _, event ->
|
private val editingTouchHandler = OnTouchListener { _, event ->
|
||||||
controls.allButtons.any { button ->
|
var handled = false
|
||||||
when (event.actionMasked) {
|
|
||||||
MotionEvent.ACTION_UP,
|
|
||||||
MotionEvent.ACTION_POINTER_UP,
|
|
||||||
MotionEvent.ACTION_CANCEL -> {
|
|
||||||
if (button.isEditing) {
|
|
||||||
button.endEdit()
|
|
||||||
return@any true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
when (event.actionMasked) {
|
||||||
MotionEvent.ACTION_DOWN,
|
MotionEvent.ACTION_DOWN,
|
||||||
MotionEvent.ACTION_POINTER_DOWN -> {
|
MotionEvent.ACTION_POINTER_DOWN -> {
|
||||||
|
// Handle this event only if no other button is being edited
|
||||||
|
if (editInfo.editButton == null) {
|
||||||
|
handled = controls.allButtons.any { button ->
|
||||||
if (button.config.enabled && button.isTouched(event.x, event.y)) {
|
if (button.config.enabled && button.isTouched(event.x, event.y)) {
|
||||||
button.startEdit()
|
editInfo.editButton = button
|
||||||
|
button.startEdit(event.x, event.y)
|
||||||
performClick()
|
performClick()
|
||||||
return@any true
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MotionEvent.ACTION_MOVE -> {
|
MotionEvent.ACTION_MOVE -> {
|
||||||
if (button.isEditing) {
|
editInfo.editButton?.edit(event.x, event.y)
|
||||||
button.edit(event.x, event.y)
|
handled = true
|
||||||
return@any true
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP,
|
||||||
|
MotionEvent.ACTION_POINTER_UP,
|
||||||
|
MotionEvent.ACTION_CANCEL -> {
|
||||||
|
editInfo.editButton?.endEdit()
|
||||||
|
editInfo.editButton = null
|
||||||
|
handled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
false
|
handled.also { if (it) invalidate() }
|
||||||
}.also { handled -> if (handled) invalidate() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setOnTouchListener(playingTouchHandler)
|
setOnTouchListener(playingTouchHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setEditMode(editMode : Boolean) = setOnTouchListener(if (editMode) editingTouchHandler else playingTouchHandler)
|
fun setEditMode(editMode : EditMode) {
|
||||||
|
editInfo.editMode = editMode
|
||||||
|
setOnTouchListener(if (editMode == EditMode.None) playingTouchHandler else editingTouchHandler)
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
fun resetControls() {
|
fun resetControls() {
|
||||||
controls.allButtons.forEach {
|
controls.allButtons.forEach {
|
||||||
@ -301,7 +313,9 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
|||||||
onStickStateChangedListener = listener
|
onStickStateChangedListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getButtonProps() = controls.allButtons.map { Pair(it.buttonId, it.config.enabled) }
|
data class ButtonProp(val buttonId : ButtonId, val enabled : Boolean)
|
||||||
|
|
||||||
|
fun getButtonProps() = controls.allButtons.map { ButtonProp(it.buttonId, it.config.enabled) }
|
||||||
|
|
||||||
fun setButtonEnabled(buttonId : ButtonId, enabled : Boolean) {
|
fun setButtonEnabled(buttonId : ButtonId, enabled : Boolean) {
|
||||||
controls.allButtons.first { it.buttonId == buttonId }.config.enabled = enabled
|
controls.allButtons.first { it.buttonId == buttonId }.config.enabled = enabled
|
||||||
|
@ -29,16 +29,14 @@ class OnScreenEditActivity : AppCompatActivity() {
|
|||||||
private val binding by lazy { OnScreenEditActivityBinding.inflate(layoutInflater) }
|
private val binding by lazy { OnScreenEditActivityBinding.inflate(layoutInflater) }
|
||||||
|
|
||||||
private var fullEditVisible = true
|
private var fullEditVisible = true
|
||||||
private var editMode = false
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appSettings : AppSettings
|
lateinit var appSettings : AppSettings
|
||||||
|
|
||||||
private val closeAction : () -> Unit = {
|
private val closeAction : () -> Unit = {
|
||||||
if (editMode) {
|
if (binding.onScreenControllerView.isEditing) {
|
||||||
toggleFabVisibility(true)
|
toggleFabVisibility(true)
|
||||||
binding.onScreenControllerView.setEditMode(false)
|
binding.onScreenControllerView.setEditMode(EditMode.None)
|
||||||
editMode = false
|
|
||||||
} else {
|
} else {
|
||||||
fullEditVisible = !fullEditVisible
|
fullEditVisible = !fullEditVisible
|
||||||
toggleFabVisibility(fullEditVisible)
|
toggleFabVisibility(fullEditVisible)
|
||||||
@ -55,28 +53,32 @@ class OnScreenEditActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val editAction = {
|
private val moveAction = {
|
||||||
editMode = true
|
binding.onScreenControllerView.setEditMode(EditMode.Move)
|
||||||
binding.onScreenControllerView.setEditMode(true)
|
|
||||||
toggleFabVisibility(false)
|
toggleFabVisibility(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val toggleAction : () -> Unit = {
|
private val toggleAction : () -> Unit = {
|
||||||
val buttonProps = binding.onScreenControllerView.getButtonProps()
|
val buttonProps = binding.onScreenControllerView.getButtonProps()
|
||||||
val checkArray = buttonProps.map { it.second }.toBooleanArray()
|
val checkedButtonsArray = buttonProps.map { it.enabled }.toBooleanArray()
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setMultiChoiceItems(buttonProps.map {
|
.setMultiChoiceItems(
|
||||||
val longText = getString(it.first.long!!)
|
buttonProps.map { button ->
|
||||||
if (it.first.short == longText) longText else "$longText: ${it.first.short}"
|
val longText = getString(button.buttonId.long!!)
|
||||||
}.toTypedArray(), checkArray) { _, which, isChecked ->
|
if (button.buttonId.short == longText) longText else "$longText: ${button.buttonId.short}"
|
||||||
checkArray[which] = isChecked
|
}.toTypedArray(),
|
||||||
}.setPositiveButton(R.string.confirm) { _, _ ->
|
checkedButtonsArray
|
||||||
buttonProps.forEachIndexed { index, pair ->
|
) { _, which, isChecked ->
|
||||||
if (checkArray[index] != pair.second)
|
checkedButtonsArray[which] = isChecked
|
||||||
binding.onScreenControllerView.setButtonEnabled(pair.first, checkArray[index])
|
|
||||||
}
|
}
|
||||||
}.setNegativeButton(R.string.cancel, null)
|
.setPositiveButton(R.string.confirm) { _, _ ->
|
||||||
|
buttonProps.forEachIndexed { index, button ->
|
||||||
|
if (checkedButtonsArray[index] != button.enabled)
|
||||||
|
binding.onScreenControllerView.setButtonEnabled(button.buttonId, checkedButtonsArray[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setOnDismissListener { fullScreen() }
|
.setOnDismissListener { fullScreen() }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
@ -105,7 +107,7 @@ class OnScreenEditActivity : AppCompatActivity() {
|
|||||||
Pair(R.drawable.ic_palette, paletteAction),
|
Pair(R.drawable.ic_palette, paletteAction),
|
||||||
Pair(R.drawable.ic_restore) { binding.onScreenControllerView.resetControls() },
|
Pair(R.drawable.ic_restore) { binding.onScreenControllerView.resetControls() },
|
||||||
Pair(R.drawable.ic_toggle, toggleAction),
|
Pair(R.drawable.ic_toggle, toggleAction),
|
||||||
Pair(R.drawable.ic_edit, editAction),
|
Pair(R.drawable.ic_edit, moveAction),
|
||||||
Pair(R.drawable.ic_zoom_out) { binding.onScreenControllerView.decreaseScale() },
|
Pair(R.drawable.ic_zoom_out) { binding.onScreenControllerView.decreaseScale() },
|
||||||
Pair(R.drawable.ic_zoom_in) { binding.onScreenControllerView.increaseScale() },
|
Pair(R.drawable.ic_zoom_in) { binding.onScreenControllerView.increaseScale() },
|
||||||
Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() },
|
Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() },
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package emu.skyline.input.onscreen
|
||||||
|
|
||||||
|
enum class EditMode {
|
||||||
|
None,
|
||||||
|
Move,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small class that holds information about the current edit session
|
||||||
|
* This is used to share information between the [OnScreenControllerView] and the individual [OnScreenButton]s
|
||||||
|
*/
|
||||||
|
class OnScreenEditInfo {
|
||||||
|
/**
|
||||||
|
* The current edit mode
|
||||||
|
*/
|
||||||
|
var editMode : EditMode = EditMode.None
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The button that is currently being edited
|
||||||
|
*/
|
||||||
|
var editButton : OnScreenButton? = null
|
||||||
|
|
||||||
|
val isEditing get() = editMode != EditMode.None
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user