mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-03-12 05:25:23 -07:00
Add TouchpadView to Android
This commit is contained in:
parent
402782b4af
commit
5914ceec77
android/app/src/main
java/com/metallic/chiaki
res
assets/controls
@ -152,9 +152,9 @@ private fun maxAbs(a: Short, b: Short) = if(abs(a.toInt()) > abs(b.toInt())) a e
|
||||
private val CONTROLLER_TOUCHES_MAX = 2 // must be the same as CHIAKI_CONTROLLER_TOUCHES_MAX
|
||||
|
||||
data class ControllerTouch(
|
||||
val x: UShort = 0U,
|
||||
val y: UShort = 0U,
|
||||
val id: Byte = -1 // -1 = up
|
||||
var x: UShort = 0U,
|
||||
var y: UShort = 0U,
|
||||
var id: Byte = -1 // -1 = up
|
||||
)
|
||||
|
||||
data class ControllerState constructor(
|
||||
@ -165,7 +165,7 @@ data class ControllerState constructor(
|
||||
var leftY: Short = 0,
|
||||
var rightX: Short = 0,
|
||||
var rightY: Short = 0,
|
||||
private var touchIdNext: UByte = 0U,
|
||||
private var touchIdNext: UByte = 100U,
|
||||
var touches: Array<ControllerTouch> = arrayOf(ControllerTouch(), ControllerTouch()),
|
||||
var gyroX: Float = 0.0f,
|
||||
var gyroY: Float = 0.0f,
|
||||
@ -196,6 +196,8 @@ data class ControllerState constructor(
|
||||
val BUTTON_SHARE = (1 shl 13).toUInt()
|
||||
val BUTTON_TOUCHPAD = (1 shl 14).toUInt()
|
||||
val BUTTON_PS = (1 shl 15).toUInt()
|
||||
val TOUCHPAD_WIDTH: UShort = 1920U
|
||||
val TOUCHPAD_HEIGHT: UShort = 942U
|
||||
}
|
||||
|
||||
infix fun or(o: ControllerState) = ControllerState(
|
||||
@ -272,6 +274,34 @@ data class ControllerState constructor(
|
||||
result = 31 * result + orientW.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
fun startTouch(x: UShort, y: UShort): UByte? =
|
||||
touches
|
||||
.find { it.id < 0 }
|
||||
?.also {
|
||||
it.id = touchIdNext.toByte()
|
||||
Log.d("TouchId", "touch id next: $touchIdNext")
|
||||
touchIdNext = ((touchIdNext + 1U) and 0x7fU).toUByte()
|
||||
}?.id?.toUByte()
|
||||
|
||||
fun stopTouch(id: UByte)
|
||||
{
|
||||
touches.find {
|
||||
it.id >= 0 && it.id == id.toByte()
|
||||
}?.let {
|
||||
it.id = -1
|
||||
}
|
||||
}
|
||||
|
||||
fun setTouchPos(id: UByte, x: UShort, y: UShort): Boolean
|
||||
= touches.find {
|
||||
it.id >= 0 && it.id == id.toByte()
|
||||
}?.let {
|
||||
val r = it.x != x || it.y != y
|
||||
it.x = x
|
||||
it.y = y
|
||||
r
|
||||
} ?: false
|
||||
}
|
||||
|
||||
class QuitReason(val value: Int)
|
||||
|
@ -26,7 +26,7 @@ class TouchTracker
|
||||
if(pointerId == null)
|
||||
{
|
||||
pointerId = event.getPointerId(event.actionIndex)
|
||||
currentPosition = Vector(event.x, event.y)
|
||||
currentPosition = Vector(event.getX(event.actionIndex), event.getY(event.actionIndex))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.lib.ControllerState
|
||||
|
||||
class TouchpadView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr)
|
||||
{
|
||||
val state: ControllerState = ControllerState()
|
||||
private val pointerTouchIds = mutableMapOf<Int, UByte>()
|
||||
|
||||
var stateChangeCallback: ((ControllerState) -> Unit)? = null
|
||||
|
||||
private val drawable: Drawable?
|
||||
|
||||
init
|
||||
{
|
||||
context.theme.obtainStyledAttributes(attrs, R.styleable.TouchpadView, 0, 0).apply {
|
||||
drawable = getDrawable(R.styleable.TouchpadView_drawable)
|
||||
recycle()
|
||||
}
|
||||
isClickable = true
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas)
|
||||
{
|
||||
super.onDraw(canvas)
|
||||
if(state.touches.find { it.id >= 0 } == null)
|
||||
return
|
||||
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
|
||||
drawable?.draw(canvas)
|
||||
}
|
||||
|
||||
private fun touchX(event: MotionEvent, index: Int): UShort =
|
||||
maxOf(0U.toUShort(), minOf((ControllerState.TOUCHPAD_WIDTH - 1u).toUShort(),
|
||||
(ControllerState.TOUCHPAD_WIDTH.toFloat() * event.getX(index) / width.toFloat()).toUInt().toUShort()))
|
||||
|
||||
private fun touchY(event: MotionEvent, index: Int): UShort =
|
||||
maxOf(0U.toUShort(), minOf((ControllerState.TOUCHPAD_HEIGHT - 1u).toUShort(),
|
||||
(ControllerState.TOUCHPAD_HEIGHT.toFloat() * event.getY(index) / height.toFloat()).toUInt().toUShort()))
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean
|
||||
{
|
||||
when(event.actionMasked)
|
||||
{
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
state.startTouch(touchX(event, event.actionIndex), touchY(event, event.actionIndex))?.let {
|
||||
pointerTouchIds[event.getPointerId(event.actionIndex)] = it
|
||||
triggerStateChanged()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
|
||||
pointerTouchIds.remove(event.getPointerId(event.actionIndex))?.let {
|
||||
state.stopTouch(it)
|
||||
triggerStateChanged()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val changed = pointerTouchIds.entries.fold(false) { acc, it ->
|
||||
val index = event.findPointerIndex(it.key)
|
||||
if(index < 0)
|
||||
acc
|
||||
else
|
||||
acc || state.setTouchPos(it.value, touchX(event, index), touchY(event, index))
|
||||
}
|
||||
if(changed)
|
||||
triggerStateChanged()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun triggerStateChanged()
|
||||
{
|
||||
stateChangeCallback?.let { it(state) }
|
||||
}
|
||||
}
|
12
android/app/src/main/res/drawable/control_touchpad.xml
Normal file
12
android/app/src/main/res/drawable/control_touchpad.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="1920dp"
|
||||
android:height="942dp"
|
||||
android:viewportWidth="508"
|
||||
android:viewportHeight="249.2375">
|
||||
<path
|
||||
android:pathData="M32,0L476,0A32,32 0,0 1,508 32L508,217.238A32,32 0,0 1,476 249.238L32,249.238A32,32 0,0 1,-0 217.238L-0,32A32,32 0,0 1,32 0z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.59637"
|
||||
android:fillColor="@color/control_primary"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
@ -47,6 +47,16 @@
|
||||
app:drawableHandle="@drawable/control_analog_stick_handle"
|
||||
/>
|
||||
|
||||
<com.metallic.chiaki.touchcontrols.TouchpadView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:drawable="@drawable/control_touchpad"
|
||||
app:layout_constraintTop_toBottomOf="@id/touchpadButtonView"
|
||||
app:layout_constraintWidth_max="300dp"
|
||||
app:layout_constraintDimensionRatio="1920:942"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||
|
||||
<com.metallic.chiaki.touchcontrols.DPadView
|
||||
android:id="@+id/dpadView"
|
||||
android:layout_width="160dp"
|
||||
|
@ -11,4 +11,8 @@
|
||||
<attr name="drawableBase" format="reference" />
|
||||
<attr name="drawableHandle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TouchpadView">
|
||||
<attr name="drawable" format="reference" />
|
||||
</declare-styleable>
|
||||
</resources>
|
65
assets/controls/touchpad_surface.svg
Normal file
65
assets/controls/touchpad_surface.svg
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1920"
|
||||
height="942"
|
||||
viewBox="0 0 507.99999 249.23751"
|
||||
version="1.1"
|
||||
id="svg981"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||
sodipodi:docname="touchpad_surface.svg">
|
||||
<defs
|
||||
id="defs975" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.24748737"
|
||||
inkscape:cx="876.84187"
|
||||
inkscape:cy="725.61319"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1031"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata978">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke-width:1.59637;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
|
||||
id="rect1544"
|
||||
width="508"
|
||||
height="249.2375"
|
||||
x="-1.7763568e-15"
|
||||
y="0"
|
||||
rx="32"
|
||||
ry="32" />
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 1.8 KiB |
Loading…
x
Reference in New Issue
Block a user