mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Refactor input overlay
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import info.cemu.cemu.utils.getInvertedDrawable
|
||||
|
||||
class InputDrawable(
|
||||
resources: Resources,
|
||||
@DrawableRes inputDrawableId: Int,
|
||||
) {
|
||||
private val iconInactive: Drawable = ResourcesCompat.getDrawable(
|
||||
resources,
|
||||
inputDrawableId,
|
||||
null
|
||||
)!!
|
||||
private val iconActive: Drawable = iconInactive.getInvertedDrawable(resources)
|
||||
private var _icon = iconInactive
|
||||
val icon: Drawable
|
||||
get() = _icon
|
||||
|
||||
fun setActiveState(active: Boolean) {
|
||||
_icon = if (active) iconActive else iconInactive
|
||||
}
|
||||
|
||||
fun setAlpha(alpha: Int) {
|
||||
iconActive.alpha = alpha
|
||||
iconInactive.alpha = alpha
|
||||
}
|
||||
|
||||
fun setBounds(bounds: Rect) {
|
||||
iconActive.bounds = bounds
|
||||
iconInactive.bounds = bounds
|
||||
}
|
||||
|
||||
fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
setBounds(Rect(left, top, right, bottom))
|
||||
}
|
||||
}
|
||||
+122
-173
@@ -13,8 +13,16 @@ import android.view.MotionEvent
|
||||
import android.view.SurfaceView
|
||||
import android.view.View
|
||||
import android.view.View.OnTouchListener
|
||||
import androidx.annotation.DrawableRes
|
||||
import info.cemu.cemu.R
|
||||
import info.cemu.cemu.inputoverlay.inputs.DPadInput
|
||||
import info.cemu.cemu.inputoverlay.inputs.Input
|
||||
import info.cemu.cemu.inputoverlay.inputs.Joystick
|
||||
import info.cemu.cemu.inputoverlay.inputs.RectangleButton
|
||||
import info.cemu.cemu.inputoverlay.inputs.RoundButton
|
||||
import info.cemu.cemu.inputoverlay.inputs.innerdrawing.ButtonInnerDrawing
|
||||
import info.cemu.cemu.inputoverlay.inputs.innerdrawing.HomeButtonInnerDrawing
|
||||
import info.cemu.cemu.inputoverlay.inputs.innerdrawing.StickClickInnerDrawing
|
||||
import info.cemu.cemu.inputoverlay.inputs.innerdrawing.TextButtonInnerDrawing
|
||||
import info.cemu.cemu.nativeinterface.NativeInput
|
||||
import info.cemu.cemu.nativeinterface.NativeInput.getControllerType
|
||||
import info.cemu.cemu.nativeinterface.NativeInput.isControllerDisabled
|
||||
@@ -35,8 +43,10 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
private var currentConfiguredInput: Input? = null
|
||||
private var nativeControllerType = -1
|
||||
private var visible = false
|
||||
var controllerIndex: Int = 0
|
||||
|
||||
private var controllerIndex: Int = 0
|
||||
private var onJoystickChange: (OverlayInput, Float, Float, Float, Float) -> Unit =
|
||||
{ _, _, _, _, _ -> }
|
||||
private var overlyButtonToNativeButton: (OverlayInput) -> Int = { _ -> -1 }
|
||||
private var inputs: MutableList<Pair<OverlayInput, Input>>? = null
|
||||
private val settingsProvider: InputOverlaySettingsManager
|
||||
private val vibrator: Vibrator?
|
||||
@@ -85,7 +95,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
return
|
||||
}
|
||||
for ((overlayInput, input) in inputs!!) {
|
||||
input.reset()
|
||||
settingsProvider.saveRectangle(overlayInput, input.getBoundingRectangle())
|
||||
}
|
||||
}
|
||||
@@ -188,13 +197,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
}
|
||||
|
||||
private fun onButtonStateChange(button: OverlayInput, state: Boolean) {
|
||||
val nativeButtonId = when (nativeControllerType) {
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> overlayButtonToVPADButton(button)
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> overlayButtonToClassicButton(button)
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> overlayButtonToProButton(button)
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> overlayButtonToWiimoteButton(button)
|
||||
else -> -1
|
||||
}
|
||||
val nativeButtonId = overlyButtonToNativeButton(button)
|
||||
if (nativeButtonId == -1) {
|
||||
return
|
||||
}
|
||||
@@ -284,13 +287,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
}
|
||||
|
||||
private fun onJoystickStateChange(joystick: OverlayInput, x: Float, y: Float) {
|
||||
val onJoystickChange = when (nativeControllerType) {
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::onVPADJoystickStateChange
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::onProJoystickStateChange
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> ::onClassicJoystickStateChange
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::onWiimoteJoystickStateChange
|
||||
else -> return
|
||||
}
|
||||
val (up, down) = if (y < 0) Pair(-y, 0f) else Pair(0f, y)
|
||||
val (left, right) = if (x < 0) Pair(-x, 0f) else Pair(0f, x)
|
||||
onJoystickChange(joystick, up, down, left, right)
|
||||
@@ -300,15 +296,13 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
return settingsProvider.getInputOverlayRectangle(input, width, height, pixelDensity)
|
||||
}
|
||||
|
||||
|
||||
private fun MutableList<Pair<OverlayInput, Input>>.addRoundButton(
|
||||
button: OverlayButton,
|
||||
@DrawableRes buttonDrawable: Int,
|
||||
innerDrawing: ButtonInnerDrawing,
|
||||
) {
|
||||
add(
|
||||
button to RoundButton(
|
||||
resources,
|
||||
buttonDrawable,
|
||||
innerDrawing,
|
||||
{ onButtonStateChange(button, it) },
|
||||
currentAlpha,
|
||||
getBoundingRectangleForInput(button),
|
||||
@@ -316,16 +310,14 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<Pair<OverlayInput, Input>>.addJoystick(
|
||||
joystick: OverlayJoystick,
|
||||
@DrawableRes joystickInnerCircleDrawable: Int = R.drawable.stick_inner,
|
||||
@DrawableRes joystickBackgroundDrawable: Int = R.drawable.stick_background,
|
||||
) {
|
||||
private fun MutableList<Pair<OverlayInput, Input>>.addRoundButton(
|
||||
button: OverlayButton,
|
||||
buttonText: String = button.name,
|
||||
) = addRoundButton(button, TextButtonInnerDrawing(buttonText))
|
||||
|
||||
private fun MutableList<Pair<OverlayInput, Input>>.addJoystick(joystick: OverlayJoystick) {
|
||||
add(
|
||||
joystick to Joystick(
|
||||
resources,
|
||||
joystickBackgroundDrawable,
|
||||
joystickInnerCircleDrawable,
|
||||
{ x, y -> onJoystickStateChange(joystick, x, y) },
|
||||
currentAlpha,
|
||||
getBoundingRectangleForInput(joystick)
|
||||
@@ -336,9 +328,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
private fun MutableList<Pair<OverlayInput, Input>>.addDpad() {
|
||||
add(
|
||||
OverlayDpad.DPAD_UP to DPadInput(
|
||||
resources,
|
||||
R.drawable.dpad_background,
|
||||
R.drawable.dpad_button,
|
||||
::onButtonStateChange,
|
||||
currentAlpha,
|
||||
getBoundingRectangleForInput(OverlayDpad.DPAD_UP)
|
||||
@@ -348,12 +337,11 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
private fun MutableList<Pair<OverlayInput, Input>>.addRectangleButton(
|
||||
button: OverlayButton,
|
||||
@DrawableRes buttonDrawable: Int,
|
||||
buttonText: String = button.name,
|
||||
) {
|
||||
add(
|
||||
button to RectangleButton(
|
||||
resources,
|
||||
buttonDrawable,
|
||||
TextButtonInnerDrawing(buttonText),
|
||||
{ onButtonStateChange(button, it) },
|
||||
currentAlpha,
|
||||
getBoundingRectangleForInput(button)
|
||||
@@ -372,85 +360,48 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
}
|
||||
|
||||
nativeControllerType = getControllerType(controllerIndex)
|
||||
overlyButtonToNativeButton = when (nativeControllerType) {
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::overlayButtonToVPADButton
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> ::overlayButtonToClassicButton
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::overlayButtonToProButton
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::overlayButtonToWiimoteButton
|
||||
else -> { _ -> -1 }
|
||||
}
|
||||
onJoystickChange = when (nativeControllerType) {
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::onVPADJoystickStateChange
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::onProJoystickStateChange
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> ::onClassicJoystickStateChange
|
||||
NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::onWiimoteJoystickStateChange
|
||||
else -> { _, _, _, _, _ -> }
|
||||
}
|
||||
inputs = mutableListOf<Pair<OverlayInput, Input>>().apply {
|
||||
addRoundButton(
|
||||
OverlayButton.MINUS,
|
||||
R.drawable.button_minus,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.PLUS,
|
||||
R.drawable.button_plus,
|
||||
)
|
||||
addRoundButton(OverlayButton.MINUS, "-")
|
||||
addRoundButton(OverlayButton.PLUS, "+")
|
||||
addDpad()
|
||||
addRoundButton(
|
||||
OverlayButton.A,
|
||||
R.drawable.button_a,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.B,
|
||||
R.drawable.button_b,
|
||||
)
|
||||
addRoundButton(OverlayButton.A)
|
||||
addRoundButton(OverlayButton.B)
|
||||
addJoystick(OverlayJoystick.RIGHT)
|
||||
if (nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) {
|
||||
addRoundButton(
|
||||
OverlayButton.X,
|
||||
R.drawable.button_x,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.Y,
|
||||
R.drawable.button_y,
|
||||
)
|
||||
addRectangleButton(
|
||||
OverlayButton.ZL,
|
||||
R.drawable.button_zl,
|
||||
)
|
||||
addRectangleButton(
|
||||
OverlayButton.ZR,
|
||||
R.drawable.button_zr,
|
||||
)
|
||||
addRectangleButton(
|
||||
OverlayButton.L,
|
||||
R.drawable.button_l,
|
||||
)
|
||||
addRectangleButton(
|
||||
OverlayButton.R,
|
||||
R.drawable.button_r,
|
||||
)
|
||||
addRoundButton(OverlayButton.X)
|
||||
addRoundButton(OverlayButton.Y)
|
||||
addRectangleButton(OverlayButton.ZL)
|
||||
addRectangleButton(OverlayButton.ZR)
|
||||
addRectangleButton(OverlayButton.L)
|
||||
addRectangleButton(OverlayButton.R)
|
||||
addJoystick(OverlayJoystick.LEFT)
|
||||
}
|
||||
if (nativeControllerType == NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) {
|
||||
addRoundButton(
|
||||
OverlayButton.ONE,
|
||||
R.drawable.button_one,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.TWO,
|
||||
R.drawable.button_two,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.C,
|
||||
R.drawable.button_c,
|
||||
)
|
||||
addRectangleButton(
|
||||
OverlayButton.Z,
|
||||
R.drawable.button_z,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.HOME,
|
||||
R.drawable.button_home,
|
||||
)
|
||||
addRoundButton(OverlayButton.ONE, "1")
|
||||
addRoundButton(OverlayButton.TWO, "2")
|
||||
addRoundButton(OverlayButton.C)
|
||||
addRectangleButton(OverlayButton.Z)
|
||||
addRoundButton(OverlayButton.HOME, HomeButtonInnerDrawing())
|
||||
}
|
||||
if (nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC
|
||||
&& nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE
|
||||
) {
|
||||
addRoundButton(
|
||||
OverlayButton.L_STICK_CLICK,
|
||||
R.drawable.button_stick,
|
||||
)
|
||||
addRoundButton(
|
||||
OverlayButton.R_STICK_CLICK,
|
||||
R.drawable.button_stick,
|
||||
)
|
||||
addRoundButton(OverlayButton.L_STICK_CLICK, StickClickInnerDrawing())
|
||||
addRoundButton(OverlayButton.R_STICK_CLICK, StickClickInnerDrawing())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,90 +423,88 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
|
||||
private fun onEditPosition(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
val x = event.x.toInt()
|
||||
val y = event.y.toInt()
|
||||
for ((_, input) in inputs!!) {
|
||||
if (input.isInside(x, y)) {
|
||||
currentConfiguredInput = input
|
||||
input.enableDrawingBoundingRect(
|
||||
resources.getColor(
|
||||
R.color.purple,
|
||||
context.theme
|
||||
)
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
val configuredInput = currentConfiguredInput
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
if (configuredInput != null) {
|
||||
return false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (currentConfiguredInput != null) {
|
||||
currentConfiguredInput!!.disableDrawingBoundingRect()
|
||||
currentConfiguredInput = null
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (currentConfiguredInput != null) {
|
||||
val x = event.x.toInt()
|
||||
val y = event.y.toInt()
|
||||
currentConfiguredInput!!.moveInput(x, y, width, height)
|
||||
val x = event.x
|
||||
val y = event.y
|
||||
for ((_, input) in inputs!!) {
|
||||
if (input.isInside(x, y)) {
|
||||
currentConfiguredInput = input
|
||||
input.enableDrawingBoundingRect(
|
||||
resources.getColor(R.color.purple, context.theme)
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (configuredInput == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (event.actionMasked == MotionEvent.ACTION_UP) {
|
||||
configuredInput.disableDrawingBoundingRect()
|
||||
currentConfiguredInput = null
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.actionMasked == MotionEvent.ACTION_MOVE) {
|
||||
val x = event.x.toInt()
|
||||
val y = event.y.toInt()
|
||||
configuredInput.moveInput(x, y, width, height)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun onEditSize(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
val x = event.x.toInt()
|
||||
val y = event.y.toInt()
|
||||
for ((_, input) in inputs!!) {
|
||||
if (input.isInside(x, y)) {
|
||||
currentConfiguredInput = input
|
||||
input.enableDrawingBoundingRect(
|
||||
resources.getColor(
|
||||
R.color.red,
|
||||
context.theme
|
||||
)
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
val configuredInput = currentConfiguredInput
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (currentConfiguredInput != null) {
|
||||
currentConfiguredInput!!.disableDrawingBoundingRect()
|
||||
currentConfiguredInput = null
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
|
||||
val x = event.x
|
||||
val y = event.y
|
||||
for ((_, input) in inputs!!) {
|
||||
if (input.isInside(x, y)) {
|
||||
currentConfiguredInput = input
|
||||
input.enableDrawingBoundingRect(
|
||||
resources.getColor(R.color.red, context.theme)
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (currentConfiguredInput != null) {
|
||||
val histSize = event.historySize
|
||||
if (event.historySize >= 2) {
|
||||
val x1 = event.getHistoricalX(0)
|
||||
val y1 = event.getHistoricalY(0)
|
||||
val x2 = event.getHistoricalX(histSize - 1)
|
||||
val y2 = event.getHistoricalY(histSize - 1)
|
||||
currentConfiguredInput!!.resize(
|
||||
(x2 - x1).toInt(),
|
||||
(y2 - y1).toInt(),
|
||||
width,
|
||||
height,
|
||||
inputsMinWidthHeight
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (configuredInput == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (event.actionMasked == MotionEvent.ACTION_UP) {
|
||||
configuredInput.disableDrawingBoundingRect()
|
||||
currentConfiguredInput = null
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.actionMasked == MotionEvent.ACTION_MOVE) {
|
||||
val histSize = event.historySize
|
||||
if (event.historySize >= 2) {
|
||||
val x1 = event.getHistoricalX(0)
|
||||
val y1 = event.getHistoricalY(0)
|
||||
val x2 = event.getHistoricalX(histSize - 1)
|
||||
val y2 = event.getHistoricalY(histSize - 1)
|
||||
configuredInput.resize(
|
||||
(x2 - x1).toInt(),
|
||||
(y2 - y1).toInt(),
|
||||
width,
|
||||
height,
|
||||
inputsMinWidthHeight
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.MotionEvent
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class Joystick(
|
||||
resources: Resources,
|
||||
@DrawableRes joystickBackgroundId: Int,
|
||||
@DrawableRes innerStickId: Int,
|
||||
private val onStickStateChange: (x: Float, y: Float) -> Unit,
|
||||
private val alpha: Int,
|
||||
boundingRectangle: Rect,
|
||||
) : Input(boundingRectangle) {
|
||||
private val joystickBackground: Drawable =
|
||||
ResourcesCompat.getDrawable(resources, joystickBackgroundId, null)!!
|
||||
private val joystickDrawable = InputDrawable(resources, innerStickId)
|
||||
private var currentPointerId = -1
|
||||
private var centerX = 0
|
||||
private var centerY = 0
|
||||
private var originalCenterX = 0
|
||||
private var originalCenterY = 0
|
||||
private var radius = 0
|
||||
private var innerRadius = 0
|
||||
|
||||
init {
|
||||
configure()
|
||||
}
|
||||
|
||||
private fun updateState(pressed: Boolean, x: Float, y: Float) {
|
||||
joystickDrawable.setActiveState(pressed)
|
||||
onStickStateChange(x, y)
|
||||
val newCentreX = (centerX + radius * x).toInt()
|
||||
val newCentreY = (centerY + radius * y).toInt()
|
||||
joystickDrawable.setBounds(
|
||||
newCentreX - innerRadius,
|
||||
newCentreY - innerRadius,
|
||||
newCentreX + innerRadius,
|
||||
newCentreY + innerRadius
|
||||
)
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
centerX = rect.centerX()
|
||||
originalCenterX = centerX
|
||||
centerY = rect.centerY()
|
||||
originalCenterY = centerY
|
||||
radius = min(rect.width(), rect.height()) / 2
|
||||
innerRadius = (radius * 0.65f).toInt()
|
||||
joystickBackground.setBounds(
|
||||
centerX - radius,
|
||||
centerY - radius,
|
||||
centerX + radius,
|
||||
centerY + radius
|
||||
)
|
||||
joystickBackground.alpha = alpha
|
||||
joystickDrawable.setAlpha(alpha)
|
||||
joystickDrawable.setBounds(
|
||||
centerX - innerRadius,
|
||||
centerY - innerRadius,
|
||||
centerX + innerRadius,
|
||||
centerY + innerRadius
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTouch(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
val pointerIndex = event.actionIndex
|
||||
val x = event.getX(pointerIndex).toInt()
|
||||
val y = event.getY(pointerIndex).toInt()
|
||||
if (isInside(x, y)) {
|
||||
centerX = x
|
||||
centerY = y
|
||||
joystickBackground.setBounds(
|
||||
centerX - radius,
|
||||
centerY - radius,
|
||||
centerX + radius,
|
||||
centerY + radius
|
||||
)
|
||||
currentPointerId = event.getPointerId(pointerIndex)
|
||||
updateState(true, 0.0f, 0.0f)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
|
||||
if (currentPointerId == event.getPointerId(event.actionIndex)) {
|
||||
centerX = originalCenterX
|
||||
centerY = originalCenterY
|
||||
joystickBackground.setBounds(
|
||||
centerX - radius,
|
||||
centerY - radius,
|
||||
centerX + radius,
|
||||
centerY + radius
|
||||
)
|
||||
currentPointerId = -1
|
||||
updateState(false, 0.0f, 0.0f)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (currentPointerId == -1) {
|
||||
return false
|
||||
}
|
||||
for (i in 0 until event.pointerCount) {
|
||||
if (currentPointerId != event.getPointerId(i)) {
|
||||
continue
|
||||
}
|
||||
var x: Float = (event.getX(i) - centerX) / radius
|
||||
var y: Float = (event.getY(i) - centerY) / radius
|
||||
val norm = sqrt(x * x + y * y)
|
||||
if (norm > 1.0f) {
|
||||
x /= norm
|
||||
y /= norm
|
||||
}
|
||||
updateState(true, x, y)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun resetInput() {
|
||||
updateState(false, 0f, 0f)
|
||||
}
|
||||
|
||||
override fun drawInput(canvas: Canvas) {
|
||||
joystickBackground.draw(canvas)
|
||||
joystickDrawable.icon.draw(canvas)
|
||||
}
|
||||
|
||||
override fun isInside(x: Int, y: Int): Boolean {
|
||||
return ((x - originalCenterX) * (x - originalCenterX) + (y - originalCenterY) * (y - originalCenterY)) <= radius * radius
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
class RectangleButton(
|
||||
resources: Resources,
|
||||
@DrawableRes buttonId: Int,
|
||||
onButtonStateChange: (state: Boolean) -> Unit,
|
||||
private val alpha: Int,
|
||||
rect: Rect,
|
||||
) : Button(resources, buttonId, onButtonStateChange, rect) {
|
||||
var left: Int = 0
|
||||
var top: Int = 0
|
||||
var right: Int = 0
|
||||
var bottom: Int = 0
|
||||
|
||||
init {
|
||||
configure()
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
this.left = rect.left
|
||||
this.top = rect.top
|
||||
this.right = rect.right
|
||||
this.bottom = rect.bottom
|
||||
inputDrawable.setAlpha(alpha)
|
||||
inputDrawable.setBounds(left, top, right, bottom)
|
||||
}
|
||||
|
||||
override fun isInside(x: Int, y: Int): Boolean {
|
||||
return x in left..<right && y in top..<bottom
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import androidx.annotation.DrawableRes
|
||||
import kotlin.math.min
|
||||
|
||||
class RoundButton constructor(
|
||||
resources: Resources,
|
||||
@DrawableRes buttonId: Int,
|
||||
onButtonStateChange: (state: Boolean) -> Unit,
|
||||
private val alpha: Int,
|
||||
rect: Rect,
|
||||
) : Button(resources, buttonId, onButtonStateChange, rect) {
|
||||
private var centreX: Int = 0
|
||||
private var centreY: Int = 0
|
||||
private var radius2: Int = 0
|
||||
|
||||
init {
|
||||
configure()
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
centreX = rect.centerX()
|
||||
centreY = rect.centerY()
|
||||
val radius = min(rect.width(), rect.height()) / 2
|
||||
inputDrawable.setBounds(
|
||||
centreX - radius,
|
||||
centreY - radius,
|
||||
centreX + radius,
|
||||
centreY + radius
|
||||
)
|
||||
inputDrawable.setAlpha(alpha)
|
||||
radius2 = radius * radius
|
||||
}
|
||||
|
||||
override fun isInside(x: Int, y: Int): Boolean {
|
||||
return ((x - centreX) * (x - centreX) + (y - centreY) * (y - centreY)) <= radius2
|
||||
}
|
||||
}
|
||||
+5
-17
@@ -1,29 +1,21 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
package info.cemu.cemu.inputoverlay.inputs
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.view.MotionEvent
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
abstract class Button(
|
||||
resources: Resources,
|
||||
@DrawableRes buttonId: Int,
|
||||
private val onButtonStateChange: (state: Boolean) -> Unit,
|
||||
boundingRect: Rect,
|
||||
) : Input(boundingRect) {
|
||||
|
||||
|
||||
protected val inputDrawable = InputDrawable(resources, buttonId)
|
||||
|
||||
protected var state = false
|
||||
private var currentPointerId: Int = -1
|
||||
|
||||
override fun onTouch(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
val pointerIndex = event.actionIndex
|
||||
val x = event.getX(pointerIndex).toInt()
|
||||
val y = event.getY(pointerIndex).toInt()
|
||||
val x = event.getX(pointerIndex)
|
||||
val y = event.getY(pointerIndex)
|
||||
if (isInside(x, y)) {
|
||||
currentPointerId = event.getPointerId(pointerIndex)
|
||||
updateState(true)
|
||||
@@ -42,16 +34,12 @@ abstract class Button(
|
||||
return false
|
||||
}
|
||||
|
||||
override fun drawInput(canvas: Canvas) {
|
||||
inputDrawable.icon.draw(canvas)
|
||||
}
|
||||
|
||||
override fun resetInput() {
|
||||
updateState(false)
|
||||
}
|
||||
|
||||
private fun updateState(state: Boolean) {
|
||||
inputDrawable.setActiveState(state)
|
||||
this.state = state
|
||||
onButtonStateChange(state)
|
||||
}
|
||||
}
|
||||
+73
-58
@@ -1,74 +1,72 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
package info.cemu.cemu.inputoverlay.inputs
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.view.MotionEvent
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import info.cemu.cemu.inputoverlay.OverlayDpad
|
||||
import info.cemu.cemu.utils.fillCircleWithStroke
|
||||
import info.cemu.cemu.utils.fillRoundRectangleWithStroke
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.min
|
||||
|
||||
class DPadInput(
|
||||
resources: Resources,
|
||||
@DrawableRes backgroundId: Int,
|
||||
@DrawableRes buttonId: Int,
|
||||
private val onButtonStateChange: (button: OverlayDpad, state: Boolean) -> Unit,
|
||||
private val alpha: Int,
|
||||
rect: Rect,
|
||||
) : Input(rect) {
|
||||
private val dpadUpDrawable = InputDrawable(resources, buttonId)
|
||||
private val dpadDownDrawable = InputDrawable(resources, buttonId)
|
||||
private val dpadLeftDrawable = InputDrawable(resources, buttonId)
|
||||
private val dpadRightDrawable = InputDrawable(resources, buttonId)
|
||||
private val background = ResourcesCompat.getDrawable(resources, backgroundId, null)!!
|
||||
private var backgroundFillColor: Int = 0
|
||||
private var backgroundStrokeColor: Int = 0
|
||||
|
||||
private val dpadUpRect = RectF()
|
||||
private val dpadDownRect = RectF()
|
||||
private val dpadLeftRect = RectF()
|
||||
private val dpadRightRect = RectF()
|
||||
|
||||
private var dpadState: Int = NONE
|
||||
|
||||
private var centerX: Int = 0
|
||||
private var centerY: Int = 0
|
||||
private var radius2: Int = 0
|
||||
private var centerX: Float = 0f
|
||||
private var centerY: Float = 0f
|
||||
private var radius2: Float = 0f
|
||||
private var radius: Float = 0f
|
||||
private var currentPointerId: Int = -1
|
||||
|
||||
override fun configure() {
|
||||
this.centerX = rect.centerX()
|
||||
this.centerY = rect.centerY()
|
||||
val radius = min(rect.width(), rect.height()) / 2
|
||||
radius2 = radius * radius
|
||||
background.alpha = alpha
|
||||
background.setBounds(
|
||||
centerX - radius,
|
||||
centerY - radius,
|
||||
centerX + radius,
|
||||
centerY + radius
|
||||
)
|
||||
backgroundFillColor = Color.argb(alpha, 128, 128, 128)
|
||||
backgroundStrokeColor = Color.argb(alpha, 200, 200, 200)
|
||||
configureColors(alpha)
|
||||
|
||||
centerX = rect.exactCenterX()
|
||||
centerY = rect.exactCenterY()
|
||||
radius = min(rect.width(), rect.height()) * 0.5f
|
||||
radius2 = radius * radius
|
||||
val buttonSize = radius / 2
|
||||
val configureButton = { circleXPos: Int, circleYPos: Int, inputDrawable: InputDrawable ->
|
||||
inputDrawable.setAlpha(alpha)
|
||||
val left = circleXPos - buttonSize / 2
|
||||
val top = circleYPos - buttonSize / 2
|
||||
inputDrawable.setBounds(left, top, left + buttonSize, top + buttonSize)
|
||||
val configureButtonRect = { circleXPos: Float, circleYPos: Float, rect: RectF ->
|
||||
val left = circleXPos - buttonSize * 0.5f
|
||||
val top = circleYPos - buttonSize * 0.5f
|
||||
rect.set(left, top, left + buttonSize, top + buttonSize)
|
||||
}
|
||||
configureButton(
|
||||
val buttonCenterXYTranslate = 2f * radius / 3f
|
||||
configureButtonRect(
|
||||
centerX,
|
||||
centerY - 2 * radius / 3,
|
||||
dpadUpDrawable
|
||||
centerY - buttonCenterXYTranslate,
|
||||
dpadUpRect
|
||||
)
|
||||
configureButton(
|
||||
configureButtonRect(
|
||||
centerX,
|
||||
centerY + 2 * radius / 3,
|
||||
dpadDownDrawable
|
||||
centerY + buttonCenterXYTranslate,
|
||||
dpadDownRect
|
||||
)
|
||||
configureButton(
|
||||
centerX - 2 * radius / 3,
|
||||
configureButtonRect(
|
||||
centerX - buttonCenterXYTranslate,
|
||||
centerY,
|
||||
dpadLeftDrawable
|
||||
dpadLeftRect
|
||||
)
|
||||
configureButton(
|
||||
centerX + 2 * radius / 3,
|
||||
configureButtonRect(
|
||||
centerX + buttonCenterXYTranslate,
|
||||
centerY,
|
||||
dpadRightDrawable
|
||||
dpadRightRect
|
||||
)
|
||||
}
|
||||
|
||||
@@ -87,23 +85,19 @@ class DPadInput(
|
||||
if (dpadState == NONE) return
|
||||
if ((dpadState and UP) != 0) {
|
||||
onButtonStateChange(OverlayDpad.DPAD_UP, pressed)
|
||||
dpadUpDrawable.setActiveState(pressed)
|
||||
}
|
||||
if ((dpadState and DOWN) != 0) {
|
||||
onButtonStateChange(OverlayDpad.DPAD_DOWN, pressed)
|
||||
dpadDownDrawable.setActiveState(pressed)
|
||||
}
|
||||
if ((dpadState and LEFT) != 0) {
|
||||
onButtonStateChange(OverlayDpad.DPAD_LEFT, pressed)
|
||||
dpadLeftDrawable.setActiveState(pressed)
|
||||
}
|
||||
if ((dpadState and RIGHT) != 0) {
|
||||
onButtonStateChange(OverlayDpad.DPAD_RIGHT, pressed)
|
||||
dpadRightDrawable.setActiveState(pressed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStateByPosition(x: Int, y: Int): Int {
|
||||
private fun getStateByPosition(x: Float, y: Float): Int {
|
||||
val norm2 = (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)
|
||||
if (norm2 <= radius2 * 0.1f) {
|
||||
return NONE
|
||||
@@ -128,8 +122,8 @@ class DPadInput(
|
||||
return false
|
||||
}
|
||||
val pointerIndex = event.actionIndex
|
||||
val x = event.getX(pointerIndex).toInt()
|
||||
val y = event.getY(pointerIndex).toInt()
|
||||
val x = event.getX(pointerIndex)
|
||||
val y = event.getY(pointerIndex)
|
||||
val pointerId = event.getPointerId(pointerIndex)
|
||||
if (isInside(x, y)) {
|
||||
currentPointerId = pointerId
|
||||
@@ -155,8 +149,8 @@ class DPadInput(
|
||||
if (currentPointerId != event.getPointerId(i)) {
|
||||
continue
|
||||
}
|
||||
val x = event.getX(i).toInt()
|
||||
val y = event.getY(i).toInt()
|
||||
val x = event.getX(i)
|
||||
val y = event.getY(i)
|
||||
updateState(getStateByPosition(x, y))
|
||||
return true
|
||||
}
|
||||
@@ -169,19 +163,40 @@ class DPadInput(
|
||||
updateState(NONE)
|
||||
}
|
||||
|
||||
override fun isInside(x: Int, y: Int): Boolean {
|
||||
override fun isInside(x: Float, y: Float): Boolean {
|
||||
return (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) <= radius2
|
||||
}
|
||||
|
||||
override fun drawInput(canvas: Canvas) {
|
||||
background.draw(canvas)
|
||||
dpadUpDrawable.icon.draw(canvas)
|
||||
dpadDownDrawable.icon.draw(canvas)
|
||||
dpadLeftDrawable.icon.draw(canvas)
|
||||
dpadRightDrawable.icon.draw(canvas)
|
||||
canvas.fillCircleWithStroke(
|
||||
centerX,
|
||||
centerY,
|
||||
radius,
|
||||
paint,
|
||||
backgroundFillColor,
|
||||
backgroundStrokeColor
|
||||
)
|
||||
drawButton(canvas, dpadUpRect, dpadState and UP)
|
||||
drawButton(canvas, dpadDownRect, dpadState and DOWN)
|
||||
drawButton(canvas, dpadLeftRect, dpadState and LEFT)
|
||||
drawButton(canvas, dpadRightRect, dpadState and RIGHT)
|
||||
}
|
||||
|
||||
private fun drawButton(canvas: Canvas, rect: RectF, state: Int) {
|
||||
val fillColor: Int
|
||||
val strokeColor: Int
|
||||
if (state != 0) {
|
||||
fillColor = activeFillColor
|
||||
strokeColor = activeStrokeColor
|
||||
} else {
|
||||
fillColor = inactiveFillColor
|
||||
strokeColor = inactiveStrokeColor
|
||||
}
|
||||
canvas.fillRoundRectangleWithStroke(rect, BUTTON_RADIUS, paint, fillColor, strokeColor)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BUTTON_RADIUS = 5f
|
||||
private const val NONE = 0
|
||||
private const val UP = 1 shl 0
|
||||
private const val DOWN = 1 shl 1
|
||||
+17
-2
@@ -1,6 +1,7 @@
|
||||
package info.cemu.cemu.inputoverlay
|
||||
package info.cemu.cemu.inputoverlay.inputs
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.view.MotionEvent
|
||||
@@ -13,6 +14,13 @@ abstract class Input protected constructor(
|
||||
) {
|
||||
private var drawBoundingRectangle = false
|
||||
private val boundingRectanglePaint = Paint()
|
||||
protected var activeFillColor = 0
|
||||
protected var activeStrokeColor = 0
|
||||
protected var inactiveFillColor = 0
|
||||
protected var inactiveStrokeColor = 0
|
||||
protected val paint = Paint().apply {
|
||||
strokeWidth = 3f
|
||||
}
|
||||
|
||||
fun getBoundingRectangle() = rect
|
||||
|
||||
@@ -65,6 +73,13 @@ abstract class Input protected constructor(
|
||||
|
||||
protected abstract fun configure()
|
||||
|
||||
protected fun configureColors(alpha: Int) {
|
||||
activeFillColor = Color.argb(alpha, 255, 255, 255)
|
||||
activeStrokeColor = Color.argb(alpha, 0, 0, 0)
|
||||
inactiveFillColor = activeStrokeColor
|
||||
inactiveStrokeColor = activeFillColor
|
||||
}
|
||||
|
||||
protected abstract fun drawInput(canvas: Canvas)
|
||||
|
||||
fun draw(canvas: Canvas) {
|
||||
@@ -83,5 +98,5 @@ abstract class Input protected constructor(
|
||||
drawBoundingRectangle = false
|
||||
}
|
||||
|
||||
abstract fun isInside(x: Int, y: Int): Boolean
|
||||
abstract fun isInside(x: Float, y: Float): Boolean
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.view.MotionEvent
|
||||
import info.cemu.cemu.utils.fillCircleWithStroke
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class Joystick(
|
||||
private val onStickStateChange: (x: Float, y: Float) -> Unit,
|
||||
private val alpha: Int,
|
||||
boundingRectangle: Rect,
|
||||
) : Input(boundingRectangle) {
|
||||
private var backgroundFillColor: Int = 0
|
||||
private var currentPointerId = -1
|
||||
private var pressed = false
|
||||
private var centerX: Float = 0f
|
||||
private var centerY: Float = 0f
|
||||
private var originalCenterX: Float = 0f
|
||||
private var originalCenterY: Float = 0f
|
||||
private var radius: Float = 0f
|
||||
private var innerRadius: Float = 0f
|
||||
private var innerCenterX: Float = 0f
|
||||
private var innerCenterY: Float = 0f
|
||||
|
||||
init {
|
||||
configure()
|
||||
}
|
||||
|
||||
private fun updateState(pressed: Boolean, x: Float, y: Float) {
|
||||
this.pressed = pressed
|
||||
onStickStateChange(x, y)
|
||||
innerCenterX = centerX + radius * x
|
||||
innerCenterY = centerY + radius * y
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
backgroundFillColor = Color.argb(alpha, 128, 128, 128)
|
||||
configureColors(alpha)
|
||||
|
||||
centerX = rect.exactCenterX()
|
||||
originalCenterX = centerX
|
||||
innerCenterX = centerX
|
||||
centerY = rect.exactCenterY()
|
||||
originalCenterY = centerY
|
||||
innerCenterY = centerY
|
||||
radius = min(rect.width(), rect.height()) * 0.5f
|
||||
innerRadius = radius * 0.65f
|
||||
}
|
||||
|
||||
override fun onTouch(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
val pointerIndex = event.actionIndex
|
||||
val x = event.getX(pointerIndex)
|
||||
val y = event.getY(pointerIndex)
|
||||
if (isInside(x, y)) {
|
||||
centerX = x
|
||||
centerY = y
|
||||
currentPointerId = event.getPointerId(pointerIndex)
|
||||
updateState(true, 0.0f, 0.0f)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
|
||||
if (currentPointerId == event.getPointerId(event.actionIndex)) {
|
||||
centerX = originalCenterX
|
||||
centerY = originalCenterY
|
||||
currentPointerId = -1
|
||||
updateState(false, 0.0f, 0.0f)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (currentPointerId == -1) {
|
||||
return false
|
||||
}
|
||||
for (i in 0 until event.pointerCount) {
|
||||
if (currentPointerId != event.getPointerId(i)) {
|
||||
continue
|
||||
}
|
||||
var x: Float = (event.getX(i) - centerX) / radius
|
||||
var y: Float = (event.getY(i) - centerY) / radius
|
||||
val norm = sqrt(x * x + y * y)
|
||||
if (norm > 1.0f) {
|
||||
x /= norm
|
||||
y /= norm
|
||||
}
|
||||
updateState(true, x, y)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun resetInput() {
|
||||
updateState(false, 0f, 0f)
|
||||
}
|
||||
|
||||
override fun drawInput(canvas: Canvas) {
|
||||
paint.color = backgroundFillColor
|
||||
paint.style = Paint.Style.FILL
|
||||
canvas.drawCircle(centerX, centerY, radius, paint)
|
||||
|
||||
val fillColor: Int
|
||||
val strokeColor: Int
|
||||
if (pressed) {
|
||||
fillColor = activeFillColor
|
||||
strokeColor = activeStrokeColor
|
||||
} else {
|
||||
fillColor = inactiveFillColor
|
||||
strokeColor = inactiveStrokeColor
|
||||
}
|
||||
|
||||
canvas.fillCircleWithStroke(
|
||||
innerCenterX,
|
||||
innerCenterY,
|
||||
innerRadius,
|
||||
paint,
|
||||
fillColor,
|
||||
strokeColor
|
||||
)
|
||||
}
|
||||
|
||||
override fun isInside(x: Float, y: Float): Boolean {
|
||||
return (x - originalCenterX) * (x - originalCenterX) + (y - originalCenterY) * (y - originalCenterY) <= radius * radius
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import info.cemu.cemu.inputoverlay.inputs.innerdrawing.ButtonInnerDrawing
|
||||
import info.cemu.cemu.utils.fillRoundRectangleWithStroke
|
||||
|
||||
class RectangleButton(
|
||||
private val innerDrawing: ButtonInnerDrawing,
|
||||
onButtonStateChange: (state: Boolean) -> Unit,
|
||||
private val alpha: Int,
|
||||
rect: Rect,
|
||||
) : Button(onButtonStateChange, rect) {
|
||||
var left: Float = 0f
|
||||
var top: Float = 0f
|
||||
var right: Float = 0f
|
||||
var bottom: Float = 0f
|
||||
|
||||
init {
|
||||
configure()
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
this.left = rect.left.toFloat()
|
||||
this.top = rect.top.toFloat()
|
||||
this.right = rect.right.toFloat()
|
||||
this.bottom = rect.bottom.toFloat()
|
||||
configureColors(alpha)
|
||||
innerDrawing.configure(rect, alpha)
|
||||
}
|
||||
|
||||
override fun drawInput(canvas: Canvas) {
|
||||
val fillColor: Int
|
||||
val strokeColor: Int
|
||||
if (state) {
|
||||
fillColor = activeFillColor
|
||||
strokeColor = activeStrokeColor
|
||||
} else {
|
||||
fillColor = inactiveFillColor
|
||||
strokeColor = inactiveStrokeColor
|
||||
}
|
||||
canvas.fillRoundRectangleWithStroke(
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
RECTANGLE_RADIUS,
|
||||
paint,
|
||||
fillColor,
|
||||
strokeColor
|
||||
)
|
||||
innerDrawing.draw(canvas, state)
|
||||
}
|
||||
|
||||
override fun isInside(x: Float, y: Float): Boolean {
|
||||
return x in left..<right && y in top..<bottom
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val RECTANGLE_RADIUS = 5f
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import info.cemu.cemu.inputoverlay.inputs.innerdrawing.ButtonInnerDrawing
|
||||
import info.cemu.cemu.utils.fillCircleWithStroke
|
||||
import kotlin.math.min
|
||||
|
||||
class RoundButton(
|
||||
private val innerDrawing: ButtonInnerDrawing,
|
||||
onButtonStateChange: (state: Boolean) -> Unit,
|
||||
private val alpha: Int,
|
||||
rect: Rect,
|
||||
) : Button(onButtonStateChange, rect) {
|
||||
private var centerX: Float = 0f
|
||||
private var centerY: Float = 0f
|
||||
private var radius2: Float = 0f
|
||||
private var radius: Float = 0f
|
||||
|
||||
init {
|
||||
configure()
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
centerX = rect.centerX().toFloat()
|
||||
centerY = rect.centerY().toFloat()
|
||||
radius = min(rect.width().toFloat(), rect.height().toFloat()) / 2f
|
||||
radius2 = radius * radius
|
||||
innerDrawing.configure(rect, alpha)
|
||||
configureColors(alpha)
|
||||
}
|
||||
|
||||
override fun drawInput(canvas: Canvas) {
|
||||
val fillColor: Int
|
||||
val strokeColor: Int
|
||||
if (state) {
|
||||
fillColor = activeFillColor
|
||||
strokeColor = activeStrokeColor
|
||||
} else {
|
||||
fillColor = inactiveFillColor
|
||||
strokeColor = inactiveStrokeColor
|
||||
}
|
||||
canvas.fillCircleWithStroke(centerX, centerY, radius, paint, fillColor, strokeColor)
|
||||
innerDrawing.draw(canvas, state)
|
||||
}
|
||||
|
||||
override fun isInside(x: Float, y: Float): Boolean {
|
||||
return (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) <= radius2
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs.innerdrawing
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
|
||||
interface ButtonInnerDrawing {
|
||||
fun draw(canvas: Canvas, state: Boolean)
|
||||
fun configure(boundingRect: Rect, alpha: Int)
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs.innerdrawing
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.Rect
|
||||
import kotlin.math.min
|
||||
|
||||
class HomeButtonInnerDrawing : ButtonInnerDrawing {
|
||||
private var activeFillColor = 0
|
||||
private var inactiveFillColor = 0
|
||||
private val paint = Paint()
|
||||
private var path = Path()
|
||||
override fun draw(canvas: Canvas, state: Boolean) {
|
||||
paint.color = if (state) activeFillColor else inactiveFillColor
|
||||
canvas.drawPath(path, paint)
|
||||
}
|
||||
|
||||
override fun configure(boundingRect: Rect, alpha: Int) {
|
||||
activeFillColor = Color.argb(alpha, 0, 0, 0)
|
||||
inactiveFillColor = Color.argb(alpha, 255, 255, 255)
|
||||
|
||||
path = Path()
|
||||
val firstPoint = IconPathPoints[0]
|
||||
path.moveTo(firstPoint.first, firstPoint.second)
|
||||
for (i in 1..<IconPathPoints.size) {
|
||||
val point = IconPathPoints[i]
|
||||
path.lineTo(point.first, point.second)
|
||||
}
|
||||
val transformMatrix = Matrix()
|
||||
val rectSize = min(boundingRect.width(), boundingRect.height()).toFloat()
|
||||
val scale = rectSize / CANVAS_SIZE
|
||||
transformMatrix.setScale(scale, scale)
|
||||
transformMatrix.postTranslate(
|
||||
boundingRect.exactCenterX() - rectSize * 0.5f,
|
||||
boundingRect.exactCenterY() - rectSize * 0.5f
|
||||
)
|
||||
path.transform(transformMatrix)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CANVAS_SIZE = 24f
|
||||
val IconPathPoints = listOf(
|
||||
6.735f to 19f,
|
||||
6.735f to 11.85f,
|
||||
4.675f to 11.85f,
|
||||
12.175f to 5.15f,
|
||||
19.675f to 11.85f,
|
||||
17.615f to 11.85f,
|
||||
17.615f to 19f,
|
||||
)
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs.innerdrawing
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.Rect
|
||||
import kotlin.math.min
|
||||
|
||||
class StickClickInnerDrawing : ButtonInnerDrawing {
|
||||
private var activeFillColor = 0
|
||||
private var inactiveFillColor = 0
|
||||
private val paint = Paint().apply {
|
||||
strokeWidth = 5f
|
||||
style = Paint.Style.STROKE
|
||||
}
|
||||
private var path = Path()
|
||||
|
||||
override fun draw(canvas: Canvas, state: Boolean) {
|
||||
paint.color = if (state) activeFillColor else inactiveFillColor
|
||||
canvas.drawPath(path, paint)
|
||||
}
|
||||
|
||||
override fun configure(boundingRect: Rect, alpha: Int) {
|
||||
activeFillColor = Color.argb(alpha, 0, 0, 0)
|
||||
inactiveFillColor = Color.argb(alpha, 255, 255, 255)
|
||||
|
||||
val transformMatrix = Matrix()
|
||||
|
||||
path = createArrowPath()
|
||||
transformMatrix.setTranslate(-0.15f, 0f)
|
||||
path.transform(transformMatrix)
|
||||
|
||||
transformMatrix.reset()
|
||||
val arrowPath = createArrowPath()
|
||||
transformMatrix.setRotate(180f)
|
||||
transformMatrix.postTranslate(0.15f, 0f)
|
||||
arrowPath.transform(transformMatrix)
|
||||
path.addPath(arrowPath)
|
||||
|
||||
transformMatrix.reset()
|
||||
val scale = min(boundingRect.width(), boundingRect.height()) * 0.5f
|
||||
transformMatrix.setScale(scale, scale)
|
||||
transformMatrix.postTranslate(
|
||||
boundingRect.exactCenterX(),
|
||||
boundingRect.exactCenterY()
|
||||
)
|
||||
path.transform(transformMatrix)
|
||||
}
|
||||
|
||||
private fun createArrowPath(): Path {
|
||||
val arrowPath = Path()
|
||||
arrowPath.moveTo(-0.5f, 0.5f)
|
||||
arrowPath.lineTo(0f, 0f)
|
||||
arrowPath.lineTo(-0.5f, -0.5f)
|
||||
return arrowPath
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package info.cemu.cemu.inputoverlay.inputs.innerdrawing
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import androidx.core.graphics.withTranslation
|
||||
import kotlin.math.min
|
||||
|
||||
class TextButtonInnerDrawing(private val text: String) : ButtonInnerDrawing {
|
||||
private val textPaint = TextPaint()
|
||||
private var staticLayout = createStaticLayout()
|
||||
private var textXCoordinate = 0f
|
||||
private var textYCoordinate = 0f
|
||||
private var activeFillColor = 0
|
||||
private var inactiveFillColor = 0
|
||||
|
||||
override fun draw(canvas: Canvas, state: Boolean) {
|
||||
textPaint.color = if (state) activeFillColor else inactiveFillColor
|
||||
canvas.withTranslation(textXCoordinate, textYCoordinate) {
|
||||
staticLayout.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
override fun configure(boundingRect: Rect, alpha: Int) {
|
||||
textPaint.textSize = min(boundingRect.width(), boundingRect.height()) * 0.75f
|
||||
activeFillColor = Color.argb(alpha, 0, 0, 0)
|
||||
inactiveFillColor = Color.argb(alpha, 255, 255, 255)
|
||||
staticLayout = createStaticLayout()
|
||||
textXCoordinate = boundingRect.exactCenterX() - staticLayout.width * 0.5f
|
||||
textYCoordinate = boundingRect.exactCenterY() - staticLayout.height * 0.5f
|
||||
}
|
||||
|
||||
private fun createStaticLayout(): StaticLayout {
|
||||
val textWidth = textPaint.measureText(text).toInt()
|
||||
return StaticLayout.Builder.obtain(text, 0, text.length, textPaint, textWidth)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package info.cemu.cemu.utils
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
|
||||
fun Canvas.fillCircleWithStroke(
|
||||
centerX: Float,
|
||||
centerY: Float,
|
||||
radius: Float,
|
||||
paint: Paint,
|
||||
fillColor: Int,
|
||||
strokeColor: Int,
|
||||
) {
|
||||
paint.color = fillColor
|
||||
paint.style = Paint.Style.FILL
|
||||
drawCircle(centerX, centerY, radius, paint)
|
||||
paint.color = strokeColor
|
||||
paint.style = Paint.Style.STROKE
|
||||
drawCircle(centerX, centerY, radius, paint)
|
||||
}
|
||||
|
||||
fun Canvas.fillRoundRectangleWithStroke(
|
||||
left: Float,
|
||||
top: Float,
|
||||
right: Float,
|
||||
bottom: Float,
|
||||
radius: Float,
|
||||
paint: Paint,
|
||||
fillColor: Int,
|
||||
strokeColor: Int,
|
||||
) {
|
||||
fillRoundRectangleWithStroke(
|
||||
RectF(left, top, right, bottom),
|
||||
radius,
|
||||
paint,
|
||||
fillColor,
|
||||
strokeColor,
|
||||
)
|
||||
}
|
||||
|
||||
fun Canvas.fillRoundRectangleWithStroke(
|
||||
rect: RectF,
|
||||
radius: Float,
|
||||
paint: Paint,
|
||||
fillColor: Int,
|
||||
strokeColor: Int,
|
||||
) {
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = fillColor
|
||||
drawRoundRect(rect, radius, radius, paint)
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.color = strokeColor
|
||||
drawRoundRect(rect, radius, radius, paint)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="95.036dp"
|
||||
android:height="95.036dp"
|
||||
android:viewportWidth="95.036"
|
||||
android:viewportHeight="95.036">
|
||||
<path
|
||||
android:pathData="M47.518,47.518m-47.268,0a47.268,47.268 0,1 1,94.536 0a47.268,47.268 0,1 1,-94.536 0"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:pathData="m27.626,70.075l5.656,0l14.757,-41.344l-0.585,0l13.911,41.344l6.046,0l-15.862,-45.115l-7.021,0zM36.142,56.684l22.557,0l0,-4.68l-22.557,0z"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
@@ -1,16 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="95.036dp"
|
||||
android:height="95.036dp"
|
||||
android:viewportWidth="95.036"
|
||||
android:viewportHeight="95.036">
|
||||
<path
|
||||
android:pathData="M47.518,47.518m-47.268,0a47.268,47.268 0,1 1,94.536 0a47.268,47.268 0,1 1,-94.536 0"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:pathData="m31.396,70.075l15.732,0c10.401,0 16.512,-4.875 16.512,-13.131 0,-7.606 -5.721,-12.611 -14.366,-12.611l-14.692,0l0,4.485l14.106,0c5.721,0 9.426,3.64 9.426,9.231 0,5.201 -3.12,7.411 -10.336,7.411l-10.986,0l0,-35.884l7.671,0c6.891,0 10.271,2.535 10.271,7.671 0,3.965 -2.21,6.241 -8.191,8.451l7.801,0c3.575,-1.56 5.916,-5.526 5.916,-9.881 0,-6.891 -5.526,-10.856 -15.147,-10.856l-13.716,0z"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
@@ -1,16 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="95.036dp"
|
||||
android:height="95.036dp"
|
||||
android:viewportWidth="95.036"
|
||||
android:viewportHeight="95.036">
|
||||
<path
|
||||
android:pathData="M47.518,47.518m-47.268,0a47.268,47.268 0,1 1,94.536 0a47.268,47.268 0,1 1,-94.536 0"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:pathData="m49.875,70.693c4.355,0 8.906,-0.975 13.196,-2.86l-1.235,-3.965c-3.25,1.3 -7.281,2.08 -10.726,2.08 -10.791,0 -17.942,-7.671 -17.942,-19.177 0,-10.986 6.566,-17.682 17.422,-17.682 3.64,0 7.736,0.78 11.181,2.08l1.56,-4.03c-3.315,-1.755 -7.996,-2.795 -12.416,-2.795 -13.976,0 -23.272,9.491 -23.272,23.662 0,13.586 8.906,22.687 22.232,22.687z"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
@@ -1,16 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="95.036dp"
|
||||
android:height="95.036dp"
|
||||
android:viewportWidth="95.036"
|
||||
android:viewportHeight="95.036">
|
||||
<path
|
||||
android:pathData="M47.518,47.518m-47.268,0a47.268,47.268 0,1 1,94.536 0a47.268,47.268 0,1 1,-94.536 0"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:pathData="M41.874,68.172L41.874,54.079h11.275v14.094c0,1.55 1.268,2.819 2.819,2.819h8.456c1.55,0 2.819,-1.268 2.819,-2.819v-19.731h4.792c1.297,0 1.917,-1.607 0.93,-2.452L49.4,24.764c-1.071,-0.958 -2.706,-0.958 -3.777,0L22.059,45.989c-0.958,0.846 -0.366,2.452 0.93,2.452h4.792v19.731c0,1.55 1.268,2.819 2.819,2.819h8.456c1.55,0 2.819,-1.268 2.819,-2.819z"
|
||||
android:strokeWidth="2.81871"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user