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 */ /* Other Java */
implementation 'info.debatty:java-string-similarity:2.0.0' implementation 'info.debatty:java-string-similarity:2.0.0'
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
} }
kapt { kapt {

View File

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

View File

@ -6,11 +6,14 @@
package emu.skyline.input.onscreen package emu.skyline.input.onscreen
import android.content.Context import android.content.Context
import android.graphics.Color
import emu.skyline.input.ButtonId import emu.skyline.input.ButtonId
import emu.skyline.utils.sharedPreferences import emu.skyline.utils.sharedPreferences
interface ControllerConfiguration { interface ControllerConfiguration {
var alpha : Int var alpha : Int
var textColor : Int
var backgroundColor : Int
var enabled : Boolean var enabled : Boolean
var globalScale : Float var globalScale : Float
var relativeX : Float var relativeX : Float
@ -22,6 +25,8 @@ interface ControllerConfiguration {
*/ */
class ControllerConfigurationDummy(defaultRelativeX : Float, defaultRelativeY : Float) : ControllerConfiguration { class ControllerConfigurationDummy(defaultRelativeX : Float, defaultRelativeY : Float) : ControllerConfiguration {
override var alpha : Int = 255 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 enabled = true
override var globalScale = 1f override var globalScale = 1f
override var relativeX = defaultRelativeX 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") 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 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 enabled by config(true)
override var globalScale by config(1.15f, "") override var globalScale by config(1.15f, "")
override var relativeX by config(defaultRelativeX) override var relativeX by config(defaultRelativeX)

View File

@ -292,6 +292,14 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
invalidate() invalidate()
} }
fun getTextColor() : Int {
return controls.globalTextColor
}
fun getBackGroundColor() : Int {
return controls.globalBackgroundColor
}
fun setOnButtonStateChangedListener(listener : OnButtonStateChangedListener) { fun setOnButtonStateChangedListener(listener : OnButtonStateChangedListener) {
onButtonStateChangedListener = listener onButtonStateChangedListener = listener
} }
@ -306,4 +314,18 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
controls.allButtons.first { it.buttonId == buttonId }.config.enabled = enabled controls.allButtons.first { it.buttonId == buttonId }.config.enabled = enabled
invalidate() 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.R
import emu.skyline.databinding.OnScreenEditActivityBinding import emu.skyline.databinding.OnScreenEditActivityBinding
import emu.skyline.settings.AppSettings 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 import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -41,10 +45,11 @@ class OnScreenEditActivity : AppCompatActivity() {
} }
private fun toggleFabVisibility(visible : Boolean) { private fun toggleFabVisibility(visible : Boolean) {
actions.forEach { fabMapping.forEach { (id, fab) ->
if (it.first != R.drawable.ic_close) if (id != R.drawable.ic_close) {
if (visible) fabMapping[it.first]!!.show() if (visible) fab.show()
else fabMapping[it.first]!!.hide() else fab.hide()
}
} }
} }
@ -59,30 +64,53 @@ class OnScreenEditActivity : AppCompatActivity() {
val checkArray = buttonProps.map { it.second }.toBooleanArray() val checkArray = buttonProps.map { it.second }.toBooleanArray()
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMultiChoiceItems(buttonProps.map { .setMultiChoiceItems(buttonProps.map {
val longText = getString(it.first.long!!) val longText = getString(it.first.long!!)
if (it.first.short == longText) longText else "$longText: ${it.first.short}" if (it.first.short == longText) longText else "$longText: ${it.first.short}"
}.toTypedArray(), checkArray) { _, which, isChecked -> }.toTypedArray(), checkArray) { _, which, isChecked ->
checkArray[which] = isChecked checkArray[which] = isChecked
}.setPositiveButton(R.string.confirm) { _, _ -> }.setPositiveButton(R.string.confirm) { _, _ ->
buttonProps.forEachIndexed { index, pair -> buttonProps.forEachIndexed { index, pair ->
if (checkArray[index] != pair.second) if (checkArray[index] != pair.second)
binding.onScreenControllerView.setButtonEnabled(pair.first, checkArray[index]) binding.onScreenControllerView.setButtonEnabled(pair.first, checkArray[index])
} }
}.setNegativeButton(R.string.cancel, null) }.setNegativeButton(R.string.cancel, null)
.setOnDismissListener { fullScreen() } .setOnDismissListener { fullScreen() }
.show() .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( private val actions : List<Pair<Int, () -> Unit>> = listOf(
Pair(R.drawable.ic_restore, { binding.onScreenControllerView.resetControls() }), Pair(R.drawable.ic_palette, paletteAction),
Pair(R.drawable.ic_toggle, toggleAction), Pair(R.drawable.ic_restore) { binding.onScreenControllerView.resetControls() },
Pair(R.drawable.ic_edit, editAction), Pair(R.drawable.ic_toggle, toggleAction),
Pair(R.drawable.ic_zoom_out, { binding.onScreenControllerView.decreaseScale() }), Pair(R.drawable.ic_edit, editAction),
Pair(R.drawable.ic_zoom_in, { binding.onScreenControllerView.increaseScale() }), Pair(R.drawable.ic_zoom_out) { binding.onScreenControllerView.decreaseScale() },
Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() }, Pair(R.drawable.ic_zoom_in) { binding.onScreenControllerView.increaseScale() },
Pair(R.drawable.ic_opacity_plus) { binding.onScreenControllerView.increaseOpacity() }, Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() },
Pair(R.drawable.ic_close, closeAction) Pair(R.drawable.ic_opacity_plus) { binding.onScreenControllerView.increaseOpacity() },
Pair(R.drawable.ic_close, closeAction)
) )
private val fabMapping = mutableMapOf<Int, FloatingActionButton>() private val fabMapping = mutableMapOf<Int, FloatingActionButton>()

View File

@ -7,13 +7,15 @@ package emu.skyline.input.onscreen
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.PointF import android.graphics.PointF
import android.graphics.Typeface
import android.os.SystemClock import android.os.SystemClock
import androidx.core.graphics.minus import androidx.core.graphics.minus
import emu.skyline.R import emu.skyline.R
import emu.skyline.input.ButtonId import emu.skyline.input.ButtonId
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.* import emu.skyline.input.StickId.Left
import emu.skyline.input.StickId.Right
import emu.skyline.utils.add import emu.skyline.utils.add
import emu.skyline.utils.multiply import emu.skyline.utils.multiply
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -65,6 +67,10 @@ class JoystickButton(
var shortDoubleTapped = false var shortDoubleTapped = false
private set 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 renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float, alpha : Int) = Unit
override fun render(canvas : Canvas) { override fun render(canvas : Canvas) {
@ -193,14 +199,14 @@ class TriggerButton(
class Controls(onScreenControllerView : OnScreenControllerView) { class Controls(onScreenControllerView : OnScreenControllerView) {
private val buttonA = CircularButton(onScreenControllerView, A, 0.95f, 0.65f, 0.025f) 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 buttonB = CircularButton(onScreenControllerView, B, 0.9f, 0.765f, 0.025f)
private val buttonX = CircularButton(onScreenControllerView, X, 0.9f, 0.55f, 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 buttonY = CircularButton(onScreenControllerView, Y, 0.85f, 0.65f, 0.025f)
private val buttonDpadLeft = CircularButton(onScreenControllerView, DpadLeft, 0.2f, 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 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 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) private val buttonR = RectangularButton(onScreenControllerView, R, 0.9f, 0.25f, 0.09f, 0.1f)
@ -248,4 +254,16 @@ class Controls(onScreenControllerView : OnScreenControllerView) {
set(value) { set(value) {
circularButtons.first().config.alpha = 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">Enable Haptic Feedback</string>
<string name="osc_feedback_description">Excludes joysticks and hardware controls</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_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">Setup Guide</string>
<string name="setup_guide_description">Sequentially map every stick and button</string> <string name="setup_guide_description">Sequentially map every stick and button</string>
<string name="joystick">Joystick</string> <string name="joystick">Joystick</string>

View File

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