2020-10-03 12:09:35 +02:00
|
|
|
/*
|
|
|
|
* 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.Paint
|
|
|
|
import android.graphics.Rect
|
2022-08-04 01:25:29 +02:00
|
|
|
import android.graphics.Typeface
|
|
|
|
import android.graphics.drawable.Drawable
|
|
|
|
import android.graphics.drawable.GradientDrawable
|
|
|
|
import android.graphics.drawable.LayerDrawable
|
2020-10-03 12:09:35 +02:00
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import emu.skyline.input.ButtonId
|
|
|
|
import kotlin.math.roundToInt
|
|
|
|
|
2020-10-05 12:04:57 +02:00
|
|
|
/**
|
|
|
|
* Converts relative values, such as coordinates and boundaries, to their absolute counterparts, also handles layout modifications like scaling and custom positioning
|
|
|
|
*/
|
2020-10-03 12:09:35 +02:00
|
|
|
abstract class OnScreenButton(
|
2022-08-04 01:25:29 +02:00
|
|
|
onScreenControllerView : OnScreenControllerView,
|
|
|
|
val buttonId : ButtonId,
|
|
|
|
private val defaultRelativeX : Float,
|
|
|
|
private val defaultRelativeY : Float,
|
|
|
|
private val defaultRelativeWidth : Float,
|
|
|
|
private val defaultRelativeHeight : Float,
|
|
|
|
drawableId : Int
|
2020-10-03 12:09:35 +02:00
|
|
|
) {
|
|
|
|
companion object {
|
|
|
|
/**
|
|
|
|
* Aspect ratio the default values were based on
|
|
|
|
*/
|
|
|
|
const val CONFIGURED_ASPECT_RATIO = 2074f / 874f
|
|
|
|
}
|
|
|
|
|
|
|
|
val config = if (onScreenControllerView.isInEditMode) ControllerConfigurationDummy(defaultRelativeX, defaultRelativeY)
|
|
|
|
else ControllerConfigurationImpl(onScreenControllerView.context, buttonId, defaultRelativeX, defaultRelativeY)
|
|
|
|
|
|
|
|
protected val drawable = ContextCompat.getDrawable(onScreenControllerView.context, drawableId)!!
|
|
|
|
|
2022-08-04 01:25:29 +02:00
|
|
|
internal val buttonSymbolPaint = Paint().apply {
|
|
|
|
color = config.textColor
|
|
|
|
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
|
|
|
isAntiAlias = true
|
|
|
|
}
|
2020-10-03 12:09:35 +02:00
|
|
|
private val textBoundsRect = Rect()
|
|
|
|
|
|
|
|
var relativeX = config.relativeX
|
|
|
|
var relativeY = config.relativeY
|
|
|
|
private val relativeWidth get() = defaultRelativeWidth * config.globalScale
|
|
|
|
private val relativeHeight get() = defaultRelativeHeight * config.globalScale
|
|
|
|
|
|
|
|
var width = 0
|
|
|
|
var height = 0
|
|
|
|
|
|
|
|
protected val adjustedHeight get() = width / CONFIGURED_ASPECT_RATIO
|
2020-10-05 12:04:57 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Buttons should be at bottom when device have large height than [adjustedHeight]
|
|
|
|
*/
|
|
|
|
protected val heightDiff get() = (height - adjustedHeight).coerceAtLeast(0f)
|
2020-10-03 12:09:35 +02:00
|
|
|
|
|
|
|
protected val itemWidth get() = width * relativeWidth
|
|
|
|
private val itemHeight get() = adjustedHeight * relativeHeight
|
|
|
|
|
|
|
|
val currentX get() = width * relativeX
|
|
|
|
val currentY get() = adjustedHeight * relativeY + heightDiff
|
|
|
|
|
|
|
|
private val left get() = currentX - itemWidth / 2f
|
|
|
|
private val top get() = currentY - itemHeight / 2f
|
|
|
|
|
2020-10-05 12:04:57 +02:00
|
|
|
protected val currentBounds get() = Rect(left.roundToInt(), top.roundToInt(), (left + itemWidth).roundToInt(), (top + itemHeight).roundToInt())
|
2020-10-03 12:09:35 +02:00
|
|
|
|
2020-10-05 12:04:57 +02:00
|
|
|
/**
|
|
|
|
* Keeps track of finger when there are multiple touches
|
|
|
|
*/
|
2020-10-03 12:09:35 +02:00
|
|
|
var touchPointerId = -1
|
2021-02-05 00:04:03 +01:00
|
|
|
var partnerPointerId = -1
|
2020-10-03 12:09:35 +02:00
|
|
|
|
2022-08-20 17:00:40 +02:00
|
|
|
var isPressed = false
|
|
|
|
|
2022-11-21 23:09:53 +01:00
|
|
|
var hapticFeedback = false
|
|
|
|
|
2020-10-03 12:09:35 +02:00
|
|
|
var isEditing = false
|
|
|
|
private set
|
|
|
|
|
2022-08-20 17:00:40 +02:00
|
|
|
protected open fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float, alpha : Int) {
|
2020-10-03 12:09:35 +02:00
|
|
|
buttonSymbolPaint.apply {
|
|
|
|
textSize = size
|
|
|
|
textAlign = Paint.Align.LEFT
|
2022-08-04 01:25:29 +02:00
|
|
|
color = config.textColor
|
2022-08-20 17:00:40 +02:00
|
|
|
this.alpha = alpha
|
2020-10-03 12:09:35 +02:00
|
|
|
getTextBounds(text, 0, text.length, textBoundsRect)
|
|
|
|
}
|
2020-10-05 12:04:57 +02:00
|
|
|
canvas.drawText(text, x - textBoundsRect.width() / 2f - textBoundsRect.left, y + textBoundsRect.height() / 2f - textBoundsRect.bottom, buttonSymbolPaint)
|
2020-10-03 12:09:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
open fun render(canvas : Canvas) {
|
|
|
|
val bounds = currentBounds
|
2022-08-20 17:00:40 +02:00
|
|
|
val alpha = if (isPressed) (config.alpha - 130).coerceIn(30..255) else config.alpha
|
2022-08-04 01:25:29 +02:00
|
|
|
renderColors(drawable)
|
2020-10-03 12:09:35 +02:00
|
|
|
drawable.apply {
|
|
|
|
this.bounds = bounds
|
2022-08-20 17:00:40 +02:00
|
|
|
this.alpha = alpha
|
2020-10-03 12:09:35 +02:00
|
|
|
draw(canvas)
|
|
|
|
}
|
2022-08-20 17:00:40 +02:00
|
|
|
renderCenteredText(canvas, buttonId.short!!, itemWidth.coerceAtMost(itemHeight) * 0.4f, bounds.centerX().toFloat(), bounds.centerY().toFloat(), alpha)
|
2020-10-03 12:09:35 +02:00
|
|
|
}
|
|
|
|
|
2022-08-04 01:25:29 +02:00
|
|
|
private fun renderColors(drawable : Drawable) {
|
|
|
|
when (drawable) {
|
|
|
|
is GradientDrawable -> drawable.setColor(config.backgroundColor)
|
|
|
|
is LayerDrawable -> {
|
|
|
|
for (i in 0 until drawable.numberOfLayers) renderColors(drawable.getDrawable(i))
|
|
|
|
}
|
|
|
|
else -> drawable.setTint(config.backgroundColor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-03 12:09:35 +02:00
|
|
|
abstract fun isTouched(x : Float, y : Float) : Boolean
|
|
|
|
|
2022-08-20 17:00:40 +02:00
|
|
|
open fun onFingerDown(x : Float, y : Float) {
|
|
|
|
isPressed = true
|
|
|
|
}
|
2020-10-03 12:09:35 +02:00
|
|
|
|
2022-08-20 17:00:40 +02:00
|
|
|
open fun onFingerUp(x : Float, y : Float) {
|
|
|
|
isPressed = false
|
|
|
|
}
|
2020-10-03 12:09:35 +02:00
|
|
|
|
|
|
|
fun loadConfigValues() {
|
|
|
|
relativeX = config.relativeX
|
|
|
|
relativeY = config.relativeY
|
|
|
|
}
|
|
|
|
|
|
|
|
fun startEdit() {
|
|
|
|
isEditing = true
|
|
|
|
}
|
|
|
|
|
|
|
|
open fun edit(x : Float, y : Float) {
|
|
|
|
relativeX = x / width
|
|
|
|
relativeY = (y - heightDiff) / adjustedHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
fun endEdit() {
|
|
|
|
config.relativeX = relativeX
|
|
|
|
config.relativeY = relativeY
|
|
|
|
isEditing = false
|
|
|
|
}
|
|
|
|
|
|
|
|
open fun resetRelativeValues() {
|
|
|
|
config.relativeX = defaultRelativeX
|
|
|
|
config.relativeY = defaultRelativeY
|
|
|
|
|
|
|
|
relativeX = defaultRelativeX
|
|
|
|
relativeY = defaultRelativeY
|
|
|
|
}
|
|
|
|
}
|