Skip to content

Commit

Permalink
Merge pull request #3 from Swordfish90/unstable
Browse files Browse the repository at this point in the history
Merge unstable branch into master
  • Loading branch information
Swordfish90 authored Jun 24, 2020
2 parents c330791 + 0bb77c1 commit ee5e75b
Show file tree
Hide file tree
Showing 18 changed files with 449 additions and 156 deletions.
42 changes: 16 additions & 26 deletions app/src/main/java/com/swordfish/radialgamepad/SamplePadConfigs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object SamplePadConfigs {
primaryDial = PrimaryDialConfig.Cross(0),
secondaryDials = listOf(
SecondaryDialConfig.SingleButton(
10, 1, 1f, ButtonConfig(
10, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_START,
label = "SELECT"
)
Expand All @@ -58,7 +58,7 @@ object SamplePadConfigs {
),
secondaryDials = listOf(
SecondaryDialConfig.SingleButton(
8, 1, 1f, ButtonConfig(
8, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_START,
label = "START"
)
Expand All @@ -71,30 +71,25 @@ object SamplePadConfigs {
primaryDial = PrimaryDialConfig.Cross(0),
secondaryDials = listOf(
SecondaryDialConfig.SingleButton(
2, 1, 1f, ButtonConfig(
2, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_SELECT,
label = "SELECT"
)
),
SecondaryDialConfig.SingleButton(
3, 1, 1f, ButtonConfig(
3, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_L1,
label = "L1"
)
),
SecondaryDialConfig.SingleButton(
4, 1, 1f, ButtonConfig(
4, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_L2,
label = "L2"
)
),
SecondaryDialConfig.Stick(9, 2, 2f, 1),
SecondaryDialConfig.SingleButton(8, 1, 1f,
ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_MODE,
label = "L3"
)
)
// When this stick is double tapped, it's going to fire a Button event
SecondaryDialConfig.Stick(9, 2.2f, 1, KeyEvent.KEYCODE_BUTTON_THUMBL)
)
)

Expand Down Expand Up @@ -123,30 +118,25 @@ object SamplePadConfigs {
),
secondaryDials = listOf(
SecondaryDialConfig.SingleButton(
2, 1, 1f, ButtonConfig(
2, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_R2,
label = "R2"
)
),
SecondaryDialConfig.SingleButton(
3, 1, 1f, ButtonConfig(
3, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_R1,
label = "R1"
)
),
SecondaryDialConfig.SingleButton(
4, 1, 1f, ButtonConfig(
4, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_START,
label = "START"
)
),
SecondaryDialConfig.Stick(8, 2, 2f, 2),
SecondaryDialConfig.SingleButton(10, 1, 1f,
ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_MODE,
label = "R3"
)
)
// When this stick is double tapped, it's going to fire a Button event
SecondaryDialConfig.Stick(8, 2.2f, 2, KeyEvent.KEYCODE_BUTTON_THUMBL)
)
)

Expand All @@ -156,24 +146,24 @@ object SamplePadConfigs {
primaryDial = PrimaryDialConfig.Cross(0),
secondaryDials = listOf(
SecondaryDialConfig.SingleButton(
1, 1, 1f, ButtonConfig(
1, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_SELECT,
iconId = R.drawable.ic_play
)
),
SecondaryDialConfig.SingleButton(
2, 1, 1f, ButtonConfig(
2, 1, ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_L1,
iconId = R.drawable.ic_stop
)
),
SecondaryDialConfig.SingleButton(4, 1, 1f,
SecondaryDialConfig.SingleButton(4, 1,
ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_MODE,
iconId = R.drawable.ic_volume_down
)
),
SecondaryDialConfig.SingleButton(5, 1, 1f,
SecondaryDialConfig.SingleButton(5, 1,
ButtonConfig(
id = KeyEvent.KEYCODE_BUTTON_MODE,
iconId = R.drawable.ic_volume_up
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.graphics.Canvas
import android.graphics.RectF
import com.swordfish.radialgamepad.library.dials.Dial
import com.swordfish.radialgamepad.library.event.GestureType
import com.swordfish.radialgamepad.library.math.Sector
import com.swordfish.radialgamepad.library.touchbound.EmptyTouchBound
import com.swordfish.radialgamepad.library.touchbound.TouchBound
import com.swordfish.radialgamepad.library.utils.TouchUtils
Expand All @@ -30,8 +31,8 @@ internal class DialInteractor(val dial: Dial, var touchBound: TouchBound = Empty

fun trackedPointerId() = dial.trackedPointerId()

fun measure(drawingRect: RectF) {
dial.measure(drawingRect)
fun measure(drawingRect: RectF, secondarySector: Sector? = null) {
dial.measure(drawingRect, secondarySector)
}

fun draw(canvas: Canvas) {
Expand All @@ -45,10 +46,12 @@ internal class DialInteractor(val dial: Dial, var touchBound: TouchBound = Empty
return dial.touch(TouchUtils.computeRelativeFingerPosition(goodFingers, dial.drawingBox()))
}

fun gesture(x: Float, y: Float, gestureType: GestureType) {
fun gesture(x: Float, y: Float, gestureType: GestureType): Boolean {
if (touchBound.contains(x, y)) {
val relativePosition = TouchUtils.computeRelativePosition(x, y, dial.drawingBox())
dial.gesture(relativePosition.x, relativePosition.y, gestureType)
return dial.gesture(relativePosition.x, relativePosition.y, gestureType)
}

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ package com.swordfish.radialgamepad.library
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import androidx.core.view.GestureDetectorCompat
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
import com.swordfish.radialgamepad.library.config.RadialGamePadConfig
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
Expand All @@ -34,10 +32,11 @@ import com.swordfish.radialgamepad.library.event.Event
import com.swordfish.radialgamepad.library.event.EventsSource
import com.swordfish.radialgamepad.library.event.GestureType
import com.swordfish.radialgamepad.library.touchbound.CircleTouchBound
import com.swordfish.radialgamepad.library.touchbound.SectorTouchBound
import com.swordfish.radialgamepad.library.touchbound.TouchBound
import com.swordfish.radialgamepad.library.utils.Constants
import com.swordfish.radialgamepad.library.utils.MathUtils.toRadians
import com.swordfish.radialgamepad.library.math.MathUtils.toRadians
import com.swordfish.radialgamepad.library.math.Sector
import com.swordfish.radialgamepad.library.touchbound.SectorTouchBound
import com.swordfish.radialgamepad.library.utils.MultiTapDetector
import com.swordfish.radialgamepad.library.utils.PaintUtils
import com.swordfish.radialgamepad.library.utils.PaintUtils.scale
import com.swordfish.radialgamepad.library.utils.TouchUtils
Expand Down Expand Up @@ -81,29 +80,26 @@ class RadialGamePad @JvmOverloads constructor(
}

private lateinit var primaryInteractor: DialInteractor
private lateinit var secondaryInteractors: Map<Int, DialInteractor>
private lateinit var secondaryInteractors: List<DialInteractor>

private val gestureDetector: GestureDetectorCompat = GestureDetectorCompat(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
allInteractors().forEach {
it.gesture(e.x, e.y, GestureType.DOUBLE_TAP)
}
return true
}
private val gestureDetector: MultiTapDetector = MultiTapDetector(context) { x, y, taps, isConfirmed ->
if (!isConfirmed) return@MultiTapDetector

override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
allInteractors().forEach {
it.gesture(e.x, e.y, GestureType.SINGLE_TAP)
}
return true
val gestureType = when (taps) {
1 -> GestureType.SINGLE_TAP
2 -> GestureType.DOUBLE_TAP
3 -> GestureType.TRIPLE_TAP
else -> null
} ?: return@MultiTapDetector

val updated = allInteractors().map {
it.gesture(x, y, gestureType)
}

override fun onLongPress(e: MotionEvent) {
allInteractors().forEach {
it.gesture(e.x, e.y, GestureType.LONG_PRESS)
}
if (updated.any { it }) {
postInvalidate()
}
})
}

init {
initializePrimaryInteractor(gamePadConfig.primaryDial)
Expand Down Expand Up @@ -141,10 +137,12 @@ class RadialGamePad @JvmOverloads constructor(
configuration.id,
configuration.rightDrawableId ?: R.drawable.direction_right_normal,
configuration.rightDrawableId ?: R.drawable.direction_right_pressed,
configuration.rightDrawableForegroundId,
configuration.theme ?: gamePadConfig.theme
)
is PrimaryDialConfig.Stick -> StickDial(
configuration.id,
configuration.buttonPressId,
configuration.theme ?: gamePadConfig.theme
)
is PrimaryDialConfig.PrimaryButtons -> PrimaryButtonsDial(
Expand All @@ -163,10 +161,12 @@ class RadialGamePad @JvmOverloads constructor(
val secondaryDial = when (config) {
is SecondaryDialConfig.Stick -> StickDial(
config.id,
config.buttonPressId,
config.theme ?: gamePadConfig.theme
)
is SecondaryDialConfig.SingleButton -> ButtonDial(
context,
config.spread,
config.buttonConfig,
config.theme ?: gamePadConfig.theme
)
Expand All @@ -176,11 +176,12 @@ class RadialGamePad @JvmOverloads constructor(
config.id,
config.rightDrawableId ?: R.drawable.direction_right_normal,
config.rightDrawableId ?: R.drawable.direction_right_pressed,
config.rightDrawableForegroundId,
config.theme ?: gamePadConfig.theme
)
}
config.index to DialInteractor(secondaryDial)
}.toMap()
DialInteractor(secondaryDial)
}
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
Expand Down Expand Up @@ -211,22 +212,25 @@ class RadialGamePad @JvmOverloads constructor(
val (widthMode, width) = extractModeAndDimension(widthMeasureSpec)
val (heightMode, height) = extractModeAndDimension(heightMeasureSpec)

val usableWidth = width - marginsInPixel * 2
val usableHeight = height - marginsInPixel * 2

when {
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.AT_MOST -> {
setMeasuredDimension(
width,
minOf(
height,
(width * extendedSize.height() / extendedSize.width()).roundToInt()
)
usableHeight,
(usableWidth * extendedSize.height() / extendedSize.width()).roundToInt()
) + marginsInPixel * 2
)
}
widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.EXACTLY -> {
setMeasuredDimension(
minOf(
width,
(height * extendedSize.width() / extendedSize.height()).roundToInt()
), height
usableWidth,
(usableHeight * extendedSize.width() / extendedSize.height()).roundToInt()
) + marginsInPixel * 2, height
)
}
else -> setMeasuredDimension(width, height)
Expand Down Expand Up @@ -255,10 +259,10 @@ class RadialGamePad @JvmOverloads constructor(
}

private fun measureSecondaryDials() {
gamePadConfig.secondaryDials.forEach { config ->
val (rect, bounds) = measureSecondaryDial(config)
secondaryInteractors[config.index]?.touchBound = bounds
secondaryInteractors[config.index]?.measure(rect)
gamePadConfig.secondaryDials.forEachIndexed { index, config ->
val (rect, sector) = measureSecondaryDial(config)
secondaryInteractors[index].touchBound = SectorTouchBound(sector)
secondaryInteractors[index].measure(rect, sector)
}
}

Expand All @@ -267,22 +271,22 @@ class RadialGamePad @JvmOverloads constructor(
primaryInteractor.touchBound = CircleTouchBound(center, size)
}

private fun measureSecondaryDial(config: SecondaryDialConfig): Pair<RectF, TouchBound> {
private fun measureSecondaryDial(config: SecondaryDialConfig): Pair<RectF, Sector> {
val rect = measureSecondaryDialAsSizeMultiplier(config).scale(size)
rect.offset(center.x, center.y)

val dialAngle = Constants.PI2 / dials
val dialSize = DEFAULT_SECONDARY_DIAL_SCALE * size * config.scale

val touchBound = SectorTouchBound(
val sector = Sector(
PointF(center.x, center.y),
size,
size + dialSize * config.scale,
secondaryDialRotation + config.index * dialAngle - dialAngle / 2,
secondaryDialRotation + (config.index + config.spread - 1) * dialAngle + dialAngle / 2
)

return rect to touchBound
return rect to sector
}

private fun measureSecondaryDialAsSizeMultiplier(config: SecondaryDialConfig): RectF {
Expand Down Expand Up @@ -312,7 +316,7 @@ class RadialGamePad @JvmOverloads constructor(

primaryInteractor.draw(canvas)

secondaryInteractors.values.forEach {
secondaryInteractors.forEach {
it.draw(canvas)
}
}
Expand All @@ -331,7 +335,7 @@ class RadialGamePad @JvmOverloads constructor(
}

override fun onTouchEvent(event: MotionEvent): Boolean {
gestureDetector.onTouchEvent(event)
gestureDetector.handleEvent(event)

val fingers = TouchUtils.extractFingerPositions(event)

Expand Down Expand Up @@ -363,7 +367,7 @@ class RadialGamePad @JvmOverloads constructor(
private fun allDials(): List<Dial> = allInteractors().map { it.dial }

private fun allInteractors(): List<DialInteractor> =
listOf(primaryInteractor) + secondaryInteractors.values
listOf(primaryInteractor) + secondaryInteractors

companion object {
const val DEFAULT_SECONDARY_DIAL_SCALE = 0.75f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,19 @@ sealed class PrimaryDialConfig {
data class Cross(
val id: Int,
val rightDrawableId: Int? = null,
val rightDrawableForegroundId: Int? = null,
val theme: RadialGamePadTheme? = null
) : PrimaryDialConfig()

/**
* The Stick dial represents a simple touch stick.
* @property id The control id. It is passed back to discriminate events.
* @property buttonPressId Specify a button action when the Stick is double pressed. Useful for platforms with clickable thumb sticks.
* @property theme A RadialGamePadTheme specific for this dial. If omitted the RadialGamePad one is used.
*/
data class Stick(
val id: Int,
val buttonPressId: Int? = null,
val theme: RadialGamePadTheme? = null
) : PrimaryDialConfig()

Expand Down
Loading

0 comments on commit ee5e75b

Please sign in to comment.