mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-06-01 11:18:46 +02:00
a799cb63f1
As part of this commit, a `defaultEnabled` property was added to `OnScreenButton` to determine the default visibility of buttons. This is required because L3 and R3 should be hidden by default and only enabled by the user on demand. Additionally, the buttons' mask values were added to `ButtonId` members, as adding entries in the middle of the class conflicted with the `ordinal` enum property, making it unfit to use for our purposes. Finally, the `ControllerType` class was extended with an array of optional buttons. Optional buttons represent buttons that are allowed to be displayed on screen, but shouldn't be included in the controller mapping activity.
274 lines
9.4 KiB
Kotlin
274 lines
9.4 KiB
Kotlin
/*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
*/
|
|
|
|
package emu.skyline.input.onscreen
|
|
|
|
import android.graphics.Canvas
|
|
import android.graphics.PointF
|
|
import android.os.SystemClock
|
|
import androidx.core.graphics.minus
|
|
import emu.skyline.R
|
|
import emu.skyline.input.ButtonId
|
|
import emu.skyline.input.ButtonId.*
|
|
import emu.skyline.input.StickId
|
|
import emu.skyline.input.StickId.Left
|
|
import emu.skyline.input.StickId.Right
|
|
import emu.skyline.utils.add
|
|
import emu.skyline.utils.multiply
|
|
import kotlin.math.roundToInt
|
|
|
|
open class CircularButton(
|
|
onScreenControllerView : OnScreenControllerView,
|
|
buttonId : ButtonId,
|
|
defaultRelativeX : Float,
|
|
defaultRelativeY : Float,
|
|
defaultRelativeRadiusToX : Float,
|
|
drawableId : Int = R.drawable.ic_button,
|
|
defaultEnabled : Boolean = true
|
|
) : OnScreenButton(
|
|
onScreenControllerView,
|
|
buttonId,
|
|
defaultRelativeX,
|
|
defaultRelativeY,
|
|
defaultRelativeRadiusToX * 2f,
|
|
defaultRelativeRadiusToX * CONFIGURED_ASPECT_RATIO * 2f,
|
|
drawableId,
|
|
defaultEnabled
|
|
) {
|
|
val radius get() = itemWidth / 2f
|
|
|
|
/**
|
|
* Checks if [x] and [y] are within circle
|
|
*/
|
|
override fun isTouched(x : Float, y : Float) : Boolean = (PointF(currentX, currentY) - (PointF(x, y))).length() <= radius
|
|
}
|
|
|
|
class JoystickButton(
|
|
onScreenControllerView : OnScreenControllerView,
|
|
val stickId : StickId,
|
|
defaultRelativeX : Float,
|
|
defaultRelativeY : Float,
|
|
defaultRelativeRadiusToX : Float
|
|
) : CircularButton(
|
|
onScreenControllerView,
|
|
stickId.button,
|
|
defaultRelativeX,
|
|
defaultRelativeY,
|
|
defaultRelativeRadiusToX,
|
|
R.drawable.ic_button
|
|
) {
|
|
private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, R.drawable.ic_stick)
|
|
|
|
var recenterSticks = false
|
|
private var initialTapPosition = PointF()
|
|
private var fingerDownTime = 0L
|
|
private var fingerUpTime = 0L
|
|
var shortDoubleTapped = false
|
|
private set
|
|
|
|
override fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float, alpha : Int) = Unit
|
|
|
|
override fun render(canvas : Canvas) {
|
|
super.render(canvas)
|
|
|
|
innerButton.width = width
|
|
innerButton.height = height
|
|
innerButton.render(canvas)
|
|
}
|
|
|
|
override fun onFingerDown(x : Float, y : Float) {
|
|
val relativeX = x / width
|
|
val relativeY = (y - heightDiff) / adjustedHeight
|
|
if (recenterSticks) {
|
|
this.relativeX = relativeX
|
|
this.relativeY = relativeY
|
|
}
|
|
innerButton.relativeX = relativeX
|
|
innerButton.relativeY = relativeY
|
|
|
|
// If first and second tap occur within 500 mills, then trigger stick press
|
|
val currentTime = SystemClock.elapsedRealtime()
|
|
initialTapPosition = PointF(x, y)
|
|
val firstTapDiff = fingerUpTime - fingerDownTime
|
|
val secondTapDiff = currentTime - fingerUpTime
|
|
if (firstTapDiff in 0..500 && secondTapDiff in 0..500) {
|
|
shortDoubleTapped = true
|
|
// Indicate stick being pressed with lower alpha value
|
|
isPressed = true
|
|
}
|
|
fingerDownTime = currentTime
|
|
}
|
|
|
|
override fun onFingerUp(x : Float, y : Float) {
|
|
loadConfigValues()
|
|
innerButton.relativeX = relativeX
|
|
innerButton.relativeY = relativeY
|
|
|
|
fingerUpTime = SystemClock.elapsedRealtime()
|
|
shortDoubleTapped = false
|
|
isPressed = false
|
|
}
|
|
|
|
fun onFingerMoved(x : Float, y : Float, manualMove : Boolean = true) : PointF {
|
|
val position = PointF(currentX, currentY)
|
|
var finger = PointF(x, y)
|
|
val outerToInner = finger.minus(position)
|
|
val distance = outerToInner.length()
|
|
if (distance > radius) {
|
|
// Limit distance to radius
|
|
finger = position.add(outerToInner.multiply(1f / distance * radius))
|
|
}
|
|
|
|
// If finger get moved to much, then don't trigger as joystick being pressed
|
|
if (manualMove && initialTapPosition.minus(finger).length() > radius * 0.075f) {
|
|
fingerDownTime = 0
|
|
fingerUpTime = 0
|
|
}
|
|
|
|
innerButton.relativeX = finger.x / width
|
|
innerButton.relativeY = (finger.y - heightDiff) / adjustedHeight
|
|
return finger.minus(position).multiply(1f / radius)
|
|
}
|
|
|
|
fun outerToInner() = PointF(innerButton.currentX, innerButton.currentY).minus(PointF(currentX, currentY))
|
|
|
|
fun outerToInnerRelative() = outerToInner().multiply(1f / radius)
|
|
|
|
override fun edit(x : Float, y : Float) {
|
|
super.edit(x, y)
|
|
|
|
innerButton.relativeX = relativeX
|
|
innerButton.relativeY = relativeY
|
|
}
|
|
|
|
override fun resetRelativeValues() {
|
|
super.resetRelativeValues()
|
|
|
|
innerButton.relativeX = relativeX
|
|
innerButton.relativeY = relativeY
|
|
}
|
|
}
|
|
|
|
open class RectangularButton(
|
|
onScreenControllerView : OnScreenControllerView,
|
|
buttonId : ButtonId,
|
|
defaultRelativeX : Float,
|
|
defaultRelativeY : Float,
|
|
defaultRelativeWidth : Float,
|
|
defaultRelativeHeight : Float,
|
|
drawableId : Int = R.drawable.ic_rectangular_button,
|
|
defaultEnabled : Boolean = true
|
|
) : OnScreenButton(
|
|
onScreenControllerView,
|
|
buttonId,
|
|
defaultRelativeX,
|
|
defaultRelativeY,
|
|
defaultRelativeWidth,
|
|
defaultRelativeHeight,
|
|
drawableId,
|
|
defaultEnabled
|
|
) {
|
|
override fun isTouched(x : Float, y : Float) = currentBounds.contains(x.roundToInt(), y.roundToInt())
|
|
}
|
|
|
|
class TriggerButton(
|
|
onScreenControllerView : OnScreenControllerView,
|
|
buttonId : ButtonId,
|
|
defaultRelativeX : Float,
|
|
defaultRelativeY : Float,
|
|
defaultRelativeWidth : Float,
|
|
defaultRelativeHeight : Float
|
|
) : RectangularButton(
|
|
onScreenControllerView,
|
|
buttonId,
|
|
defaultRelativeX,
|
|
defaultRelativeY,
|
|
defaultRelativeWidth,
|
|
defaultRelativeHeight,
|
|
when (buttonId) {
|
|
ZL -> R.drawable.ic_trigger_button_left
|
|
|
|
ZR -> R.drawable.ic_trigger_button_right
|
|
|
|
else -> error("Unsupported trigger button")
|
|
}
|
|
)
|
|
|
|
class Controls(onScreenControllerView : OnScreenControllerView) {
|
|
private val buttonA = CircularButton(onScreenControllerView, A, 0.95f, 0.65f, 0.025f)
|
|
private val buttonB = CircularButton(onScreenControllerView, B, 0.9f, 0.765f, 0.025f)
|
|
private val buttonX = CircularButton(onScreenControllerView, X, 0.9f, 0.535f, 0.025f)
|
|
private val buttonY = CircularButton(onScreenControllerView, Y, 0.85f, 0.65f, 0.025f)
|
|
|
|
private val buttonDpadLeft = CircularButton(onScreenControllerView, DpadLeft, 0.2f, 0.65f, 0.025f)
|
|
private val buttonDpadUp = CircularButton(onScreenControllerView, DpadUp, 0.25f, 0.535f, 0.025f)
|
|
private val buttonDpadRight = CircularButton(onScreenControllerView, DpadRight, 0.3f, 0.65f, 0.025f)
|
|
private val buttonDpadDown = CircularButton(onScreenControllerView, DpadDown, 0.25f, 0.765f, 0.025f)
|
|
|
|
private val buttonL = RectangularButton(onScreenControllerView, L, 0.1f, 0.25f, 0.09f, 0.1f)
|
|
private val buttonR = RectangularButton(onScreenControllerView, R, 0.9f, 0.25f, 0.09f, 0.1f)
|
|
|
|
private val buttonZL = TriggerButton(onScreenControllerView, ZL, 0.1f, 0.1f, 0.09f, 0.1f)
|
|
private val buttonZR = TriggerButton(onScreenControllerView, ZR, 0.9f, 0.1f, 0.09f, 0.1f)
|
|
|
|
private val buttonL3 = CircularButton(onScreenControllerView, L3, 0.35f, 0.87f, 0.025f, defaultEnabled = false)
|
|
private val buttonR3 = CircularButton(onScreenControllerView, R3, 0.65f, 0.87f, 0.025f, defaultEnabled = false)
|
|
|
|
private val circularButtonPairs = listOf(setOf(buttonA, buttonB, buttonX, buttonY), setOf(buttonDpadLeft, buttonDpadUp, buttonDpadRight, buttonDpadDown))
|
|
|
|
private val triggerButtonPairs = listOf(setOf(buttonL, buttonZL), setOf(buttonR, buttonZR))
|
|
|
|
private val stickButtons = setOf(buttonL3, buttonR3)
|
|
|
|
val buttonPairs = circularButtonPairs + triggerButtonPairs
|
|
|
|
val circularButtons = circularButtonPairs.flatten() + stickButtons + listOf(
|
|
CircularButton(onScreenControllerView, Plus, 0.57f, 0.75f, 0.025f),
|
|
CircularButton(onScreenControllerView, Minus, 0.43f, 0.75f, 0.025f),
|
|
CircularButton(onScreenControllerView, Menu, 0.5f, 0.75f, 0.025f)
|
|
)
|
|
|
|
val joysticks = listOf(
|
|
JoystickButton(onScreenControllerView, Left, 0.1f, 0.8f, 0.05f),
|
|
JoystickButton(onScreenControllerView, Right, 0.75f, 0.6f, 0.05f)
|
|
)
|
|
|
|
val rectangularButtons = listOf(buttonL, buttonR)
|
|
|
|
val triggerButtons = listOf(buttonZL, buttonZR)
|
|
|
|
val allButtons = circularButtons + joysticks + rectangularButtons + triggerButtons
|
|
|
|
/**
|
|
* We can take any of the global scale variables from the buttons since the value is shared across all buttons
|
|
*/
|
|
var globalScale
|
|
get() = circularButtons.first().config.globalScale
|
|
set(value) {
|
|
circularButtons.first().config.globalScale = value
|
|
}
|
|
|
|
/**
|
|
* We can take any of the alpha variables from the buttons since the value is shared across all buttons
|
|
*/
|
|
var alpha
|
|
get() = circularButtons.first().config.alpha
|
|
set(value) {
|
|
circularButtons.first().config.alpha = value
|
|
}
|
|
|
|
/**
|
|
* We can take any of the global text color variables from the buttons
|
|
*/
|
|
val globalTextColor
|
|
get() = circularButtons.first().config.textColor
|
|
|
|
/**
|
|
* We can take any of the global background color variables from the buttons
|
|
*/
|
|
val globalBackgroundColor
|
|
get() = circularButtons.first().config.backgroundColor
|
|
}
|