Add color selection to OSC

* Add bold text and antialiasing for osc buttons
* Fix osc dpad and button position (widder than taller)
* Set default OSC color to white background with black text
This commit is contained in:
KikiManjaro 2022-08-04 01:25:29 +02:00 committed by Niccolò Betto
parent 66d2965c63
commit 1282362fce
10 changed files with 173 additions and 40 deletions

View File

@ -184,6 +184,7 @@ dependencies {
/* Other Java */
implementation 'info.debatty:java-string-similarity:2.0.0'
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
}
kapt {

View File

@ -6,9 +6,12 @@
package emu.skyline.input.onscreen
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import androidx.core.content.ContextCompat
import emu.skyline.input.ButtonId
import kotlin.math.roundToInt
@ -17,13 +20,13 @@ import kotlin.math.roundToInt
* Converts relative values, such as coordinates and boundaries, to their absolute counterparts, also handles layout modifications like scaling and custom positioning
*/
abstract class OnScreenButton(
onScreenControllerView : OnScreenControllerView,
val buttonId : ButtonId,
private val defaultRelativeX : Float,
private val defaultRelativeY : Float,
private val defaultRelativeWidth : Float,
private val defaultRelativeHeight : Float,
drawableId : Int
onScreenControllerView : OnScreenControllerView,
val buttonId : ButtonId,
private val defaultRelativeX : Float,
private val defaultRelativeY : Float,
private val defaultRelativeWidth : Float,
private val defaultRelativeHeight : Float,
drawableId : Int
) {
companion object {
/**
@ -37,7 +40,11 @@ abstract class OnScreenButton(
protected val drawable = ContextCompat.getDrawable(onScreenControllerView.context, drawableId)!!
private val buttonSymbolPaint = Paint().apply { color = Color.GRAY }
internal val buttonSymbolPaint = Paint().apply {
color = config.textColor
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
isAntiAlias = true
}
private val textBoundsRect = Rect()
var relativeX = config.relativeX
@ -83,6 +90,7 @@ abstract class OnScreenButton(
buttonSymbolPaint.apply {
textSize = size
textAlign = Paint.Align.LEFT
color = config.textColor
this.alpha = alpha
getTextBounds(text, 0, text.length, textBoundsRect)
}
@ -92,15 +100,25 @@ abstract class OnScreenButton(
open fun render(canvas : Canvas) {
val bounds = currentBounds
val alpha = if (isPressed) (config.alpha - 130).coerceIn(30..255) else config.alpha
renderColors(drawable)
drawable.apply {
this.bounds = bounds
this.alpha = alpha
draw(canvas)
}
renderCenteredText(canvas, buttonId.short!!, itemWidth.coerceAtMost(itemHeight) * 0.4f, bounds.centerX().toFloat(), bounds.centerY().toFloat(), alpha)
}
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)
}
}
abstract fun isTouched(x : Float, y : Float) : Boolean
open fun onFingerDown(x : Float, y : Float) {

View File

@ -6,11 +6,14 @@
package emu.skyline.input.onscreen
import android.content.Context
import android.graphics.Color
import emu.skyline.input.ButtonId
import emu.skyline.utils.sharedPreferences
interface ControllerConfiguration {
var alpha : Int
var textColor : Int
var backgroundColor : Int
var enabled : Boolean
var globalScale : Float
var relativeX : Float
@ -22,6 +25,8 @@ interface ControllerConfiguration {
*/
class ControllerConfigurationDummy(defaultRelativeX : Float, defaultRelativeY : Float) : ControllerConfiguration {
override var alpha : Int = 255
override var textColor = Color.argb(180, 0, 0, 0)
override var backgroundColor = Color.argb(180, 255, 255, 255)
override var enabled = true
override var globalScale = 1f
override var relativeX = defaultRelativeX
@ -32,6 +37,8 @@ class ControllerConfigurationImpl(private val context : Context, private val but
private inline fun <reified T> config(default : T, prefix : String = "${buttonId.name}_") = sharedPreferences(context, default, prefix, "controller_config")
override var alpha by config(255, "")
override var textColor by config(Color.argb(180, 0, 0, 0))
override var backgroundColor by config(Color.argb(180, 255, 255, 255))
override var enabled by config(true)
override var globalScale by config(1.15f, "")
override var relativeX by config(defaultRelativeX)

View File

@ -292,6 +292,14 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
invalidate()
}
fun getTextColor() : Int {
return controls.globalTextColor
}
fun getBackGroundColor() : Int {
return controls.globalBackgroundColor
}
fun setOnButtonStateChangedListener(listener : OnButtonStateChangedListener) {
onButtonStateChangedListener = listener
}
@ -306,4 +314,18 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
controls.allButtons.first { it.buttonId == buttonId }.config.enabled = enabled
invalidate()
}
fun setTextColor(color : Int) {
for (button in controls.allButtons) {
button.config.textColor = color
}
invalidate()
}
fun setBackGroundColor(color : Int) {
for (button in controls.allButtons) {
button.config.backgroundColor = color
}
invalidate()
}
}

View File

@ -16,6 +16,10 @@ import dagger.hilt.android.AndroidEntryPoint
import emu.skyline.R
import emu.skyline.databinding.OnScreenEditActivityBinding
import emu.skyline.settings.AppSettings
import emu.skyline.utils.SwitchColors
import emu.skyline.utils.SwitchColors.*
import petrov.kristiyan.colorpicker.DoubleColorPicker
import petrov.kristiyan.colorpicker.DoubleColorPicker.OnChooseDoubleColorListener
import javax.inject.Inject
@AndroidEntryPoint
@ -41,10 +45,11 @@ class OnScreenEditActivity : AppCompatActivity() {
}
private fun toggleFabVisibility(visible : Boolean) {
actions.forEach {
if (it.first != R.drawable.ic_close)
if (visible) fabMapping[it.first]!!.show()
else fabMapping[it.first]!!.hide()
fabMapping.forEach { (id, fab) ->
if (id != R.drawable.ic_close) {
if (visible) fab.show()
else fab.hide()
}
}
}
@ -59,30 +64,53 @@ class OnScreenEditActivity : AppCompatActivity() {
val checkArray = buttonProps.map { it.second }.toBooleanArray()
MaterialAlertDialogBuilder(this)
.setMultiChoiceItems(buttonProps.map {
val longText = getString(it.first.long!!)
if (it.first.short == longText) longText else "$longText: ${it.first.short}"
}.toTypedArray(), checkArray) { _, which, isChecked ->
checkArray[which] = isChecked
}.setPositiveButton(R.string.confirm) { _, _ ->
buttonProps.forEachIndexed { index, pair ->
if (checkArray[index] != pair.second)
binding.onScreenControllerView.setButtonEnabled(pair.first, checkArray[index])
}
}.setNegativeButton(R.string.cancel, null)
.setOnDismissListener { fullScreen() }
.show()
.setMultiChoiceItems(buttonProps.map {
val longText = getString(it.first.long!!)
if (it.first.short == longText) longText else "$longText: ${it.first.short}"
}.toTypedArray(), checkArray) { _, which, isChecked ->
checkArray[which] = isChecked
}.setPositiveButton(R.string.confirm) { _, _ ->
buttonProps.forEachIndexed { index, pair ->
if (checkArray[index] != pair.second)
binding.onScreenControllerView.setButtonEnabled(pair.first, checkArray[index])
}
}.setNegativeButton(R.string.cancel, null)
.setOnDismissListener { fullScreen() }
.show()
}
private val paletteAction : () -> Unit = {
DoubleColorPicker(this@OnScreenEditActivity).apply {
setTitle(this@OnScreenEditActivity.getString(R.string.osc_background_color))
setDefaultColorButton(binding.onScreenControllerView.getBackGroundColor())
setRoundColorButton(true)
setColors(*SwitchColors.colors.toIntArray())
setDefaultDoubleColorButton(binding.onScreenControllerView.getTextColor())
setSecondTitle(this@OnScreenEditActivity.getString(R.string.osc_text_color))
setOnChooseDoubleColorListener(object : OnChooseDoubleColorListener {
override fun onChooseColor(position : Int, color : Int, position2 : Int, color2 : Int) {
binding.onScreenControllerView.setBackGroundColor(SwitchColors.colors[position])
binding.onScreenControllerView.setTextColor(SwitchColors.colors[position2])
}
override fun onCancel() {}
})
show()
}
}
private val actions : List<Pair<Int, () -> Unit>> = listOf(
Pair(R.drawable.ic_restore, { binding.onScreenControllerView.resetControls() }),
Pair(R.drawable.ic_toggle, toggleAction),
Pair(R.drawable.ic_edit, editAction),
Pair(R.drawable.ic_zoom_out, { binding.onScreenControllerView.decreaseScale() }),
Pair(R.drawable.ic_zoom_in, { binding.onScreenControllerView.increaseScale() }),
Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() },
Pair(R.drawable.ic_opacity_plus) { binding.onScreenControllerView.increaseOpacity() },
Pair(R.drawable.ic_close, closeAction)
Pair(R.drawable.ic_palette, paletteAction),
Pair(R.drawable.ic_restore) { binding.onScreenControllerView.resetControls() },
Pair(R.drawable.ic_toggle, toggleAction),
Pair(R.drawable.ic_edit, editAction),
Pair(R.drawable.ic_zoom_out) { binding.onScreenControllerView.decreaseScale() },
Pair(R.drawable.ic_zoom_in) { binding.onScreenControllerView.increaseScale() },
Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() },
Pair(R.drawable.ic_opacity_plus) { binding.onScreenControllerView.increaseOpacity() },
Pair(R.drawable.ic_close, closeAction)
)
private val fabMapping = mutableMapOf<Int, FloatingActionButton>()

View File

@ -7,13 +7,15 @@ package emu.skyline.input.onscreen
import android.graphics.Canvas
import android.graphics.PointF
import android.graphics.Typeface
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.*
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
@ -65,6 +67,10 @@ class JoystickButton(
var shortDoubleTapped = false
private set
init {
innerButton.buttonSymbolPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
}
override fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float, alpha : Int) = Unit
override fun render(canvas : Canvas) {
@ -193,14 +199,14 @@ class TriggerButton(
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.75f, 0.025f)
private val buttonX = CircularButton(onScreenControllerView, X, 0.9f, 0.55f, 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.55f, 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.75f, 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)
@ -248,4 +254,16 @@ class Controls(onScreenControllerView : OnScreenControllerView) {
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
}

View File

@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.utils
import android.graphics.Color
enum class SwitchColors(val color : Int) {
GRAY(Color.GRAY),
TRANSPARENT(Color.argb(180, 0, 0, 0)),
WHITE(Color.argb(180, 255, 255, 255)),
NEON_YELLOW(Color.argb(180, 230, 255, 0)),
NEON_PURPLE(Color.argb(180, 180, 0, 230)),
NEON_RED(Color.argb(180, 255, 60, 40)),
MARIO_RED(Color.argb(180, 225, 15, 0)),
NEON_BLUE(Color.argb(180, 10, 185, 230)),
BLUE(Color.argb(180, 70, 85, 245)),
NEON_GREEN(Color.argb(180, 30, 220, 0));
companion object {
val colors = SwitchColors.values().map { clr -> clr.color }
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z" />
</vector>

View File

@ -155,6 +155,8 @@
<string name="osc_feedback">Enable Haptic Feedback</string>
<string name="osc_feedback_description">Excludes joysticks and hardware controls</string>
<string name="osc_edit">Edit On-Screen Controls layout</string>
<string name="osc_text_color">Text color</string>
<string name="osc_background_color">Background color</string>
<string name="setup_guide">Setup Guide</string>
<string name="setup_guide_description">Sequentially map every stick and button</string>
<string name="joystick">Joystick</string>

View File

@ -9,6 +9,7 @@ buildscript {
repositories {
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
dependencies {
@ -28,6 +29,7 @@ allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
}