Implement toggle mode for OSC buttons

When toggle mode is enabled, the button will toggle between the `Pressed` and `Released` state when it's pressed.

Without toggle mode:
ACTION_DOWN -> Pressed
ACTION_UP -> Released
ACTION_DOWN -> Pressed
ACTION_UP -> Released

With toggle mode:
ACTION_DOWN -> Pressed
ACTION_UP -> No event
ACTION_DOWN -> No event
ACTION_UP -> Released
This commit is contained in:
lynxnb 2023-04-03 03:06:30 +02:00 committed by Billy Laws
parent 78252fbcbd
commit 560fcd9442
7 changed files with 77 additions and 15 deletions

View File

@ -14,6 +14,12 @@ interface ConfigurableButton {
val buttonId : ButtonId val buttonId : ButtonId
val config : OnScreenConfiguration val config : OnScreenConfiguration
/**
* Returns whether this button supports toggle mode
* Usually true for buttons and false for sticks
*/
fun supportsToggleMode() : Boolean = true
/** /**
* Starts a button move session * Starts a button move session
* @param x The x coordinate of the initial touch * @param x The x coordinate of the initial touch

View File

@ -107,6 +107,7 @@ abstract class OnScreenButton(
var partnerPointerId = -1 var partnerPointerId = -1
var isPressed = false var isPressed = false
var isToggled = false
var hapticFeedback = false var hapticFeedback = false
@ -167,12 +168,26 @@ abstract class OnScreenButton(
abstract fun isTouched(x : Float, y : Float) : Boolean abstract fun isTouched(x : Float, y : Float) : Boolean
open fun onFingerDown(x : Float, y : Float) { /**
isPressed = true * @return Whether button events should be sent to guest
*/
open fun onFingerDown(x : Float, y : Float) : Boolean {
if (!config.toggleMode || !isToggled)
isPressed = true
isToggled = !isToggled
return !config.toggleMode || isToggled
} }
open fun onFingerUp(x : Float, y : Float) { /**
isPressed = false * @return Whether button events should be sent to guest
*/
open fun onFingerUp(x : Float, y : Float) : Boolean {
if (!config.toggleMode || !isToggled)
isPressed = false
return !isPressed
} }
fun loadConfigValues() { fun loadConfigValues() {
@ -264,6 +279,7 @@ abstract class OnScreenButton(
override fun resetConfig() { override fun resetConfig() {
config.enabled = defaultEnabled config.enabled = defaultEnabled
config.toggleMode = OnScreenConfiguration.DefaultToggleMode
config.alpha = OnScreenConfiguration.DefaultAlpha config.alpha = OnScreenConfiguration.DefaultAlpha
config.textColor = OnScreenConfiguration.DefaultTextColor config.textColor = OnScreenConfiguration.DefaultTextColor
config.backgroundColor = OnScreenConfiguration.DefaultBackgroundColor config.backgroundColor = OnScreenConfiguration.DefaultBackgroundColor

View File

@ -16,6 +16,8 @@ interface OnScreenConfiguration {
const val GroupEnabled = 1 const val GroupEnabled = 1
const val GroupIndeterminate = 2 const val GroupIndeterminate = 2
const val DefaultToggleMode = false
const val MinAlpha = 0 const val MinAlpha = 0
const val MaxAlpha = 255 const val MaxAlpha = 255
const val DefaultAlpha = 128 const val DefaultAlpha = 128
@ -35,6 +37,13 @@ interface OnScreenConfiguration {
*/ */
val groupEnabled get() = if (enabled) GroupEnabled else GroupDisabled val groupEnabled get() = if (enabled) GroupEnabled else GroupDisabled
var toggleMode : Boolean
/**
* The toggle mode of group of buttons, returns an integer that can be used to set the state of a MaterialCheckBox
*/
val groupToggleMode get() = if (toggleMode) GroupEnabled else GroupDisabled
var alpha : Int var alpha : Int
var textColor : Int var textColor : Int
var backgroundColor : Int var backgroundColor : Int
@ -48,6 +57,7 @@ class OnScreenConfigurationImpl(private val context : Context, private val butto
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 enabled by config(defaultEnabled) override var enabled by config(defaultEnabled)
override var toggleMode by config(OnScreenConfiguration.DefaultToggleMode)
override var alpha by config(OnScreenConfiguration.DefaultAlpha) override var alpha by config(OnScreenConfiguration.DefaultAlpha)
override var textColor by config(OnScreenConfiguration.DefaultTextColor) override var textColor by config(OnScreenConfiguration.DefaultTextColor)

View File

@ -122,13 +122,13 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
MotionEvent.ACTION_POINTER_UP -> { MotionEvent.ACTION_POINTER_UP -> {
if (pointerId == button.touchPointerId) { if (pointerId == button.touchPointerId) {
button.touchPointerId = -1 button.touchPointerId = -1
button.onFingerUp(x, y) if (button.onFingerUp(x, y))
onButtonStateChangedListener?.invoke(button.buttonId, ButtonState.Released) onButtonStateChangedListener?.invoke(button.buttonId, ButtonState.Released)
handled = true handled = true
} else if (pointerId == button.partnerPointerId) { } else if (pointerId == button.partnerPointerId) {
button.partnerPointerId = -1 button.partnerPointerId = -1
button.onFingerUp(x, y) if (button.onFingerUp(x, y))
onButtonStateChangedListener?.invoke(button.buttonId, ButtonState.Released) onButtonStateChangedListener?.invoke(button.buttonId, ButtonState.Released)
handled = true handled = true
} }
} }
@ -137,10 +137,10 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
MotionEvent.ACTION_POINTER_DOWN -> { MotionEvent.ACTION_POINTER_DOWN -> {
if (button.config.enabled && button.isTouched(x, y)) { if (button.config.enabled && button.isTouched(x, y)) {
button.touchPointerId = pointerId button.touchPointerId = pointerId
button.onFingerDown(x, y) if (button.onFingerDown(x, y))
onButtonStateChangedListener?.invoke(button.buttonId, ButtonState.Pressed)
if (hapticFeedback) vibrator.vibrate(effectClick) if (hapticFeedback) vibrator.vibrate(effectClick)
performClick() performClick()
onButtonStateChangedListener?.invoke(button.buttonId, ButtonState.Pressed)
handled = true handled = true
} }
} }
@ -153,9 +153,9 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
for (otherButton in buttonPair) { for (otherButton in buttonPair) {
if (otherButton != button && otherButton.config.enabled && otherButton.isTouched(event.getX(fingerId), event.getY(fingerId))) { if (otherButton != button && otherButton.config.enabled && otherButton.isTouched(event.getX(fingerId), event.getY(fingerId))) {
otherButton.partnerPointerId = fingerId otherButton.partnerPointerId = fingerId
otherButton.onFingerDown(x, y) if (otherButton.onFingerDown(x, y))
onButtonStateChangedListener?.invoke(otherButton.buttonId, ButtonState.Pressed)
performClick() performClick()
onButtonStateChangedListener?.invoke(otherButton.buttonId, ButtonState.Pressed)
handled = true handled = true
} }
} }
@ -318,6 +318,11 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
invalidate() invalidate()
} }
fun setButtonToggleMode(toggleMode : Boolean) {
editInfo.editButton.config.toggleMode = toggleMode
invalidate()
}
fun setButtonScale(@IntRange(from = 0, to = 100) scale : Int) { fun setButtonScale(@IntRange(from = 0, to = 100) scale : Int) {
fun toScaleRange(value : Int) : Float = (value / 100f) * (OnScreenConfiguration.MaxScale - OnScreenConfiguration.MinScale) + OnScreenConfiguration.MinScale fun toScaleRange(value : Int) : Float = (value / 100f) * (OnScreenConfiguration.MaxScale - OnScreenConfiguration.MinScale) + OnScreenConfiguration.MinScale
@ -398,6 +403,21 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
return OnScreenConfiguration.GroupIndeterminate return OnScreenConfiguration.GroupIndeterminate
} }
override var toggleMode : Boolean
get() = controls.allButtons.all { it.supportsToggleMode() == it.config.toggleMode }
set(value) {
controls.allButtons.forEach { if (it.supportsToggleMode()) it.config.toggleMode = value }
}
override val groupToggleMode : Int
get() {
if (controls.allButtons.all { !it.supportsToggleMode() || it.config.toggleMode })
return OnScreenConfiguration.GroupEnabled
if (controls.allButtons.all { !it.supportsToggleMode() || !it.config.toggleMode })
return OnScreenConfiguration.GroupDisabled
return OnScreenConfiguration.GroupIndeterminate
}
override var alpha : Int override var alpha : Int
get() = controls.allButtons.sumOf { it.config.alpha } / controls.allButtons.size get() = controls.allButtons.sumOf { it.config.alpha } / controls.allButtons.size
set(value) { set(value) {

View File

@ -123,6 +123,9 @@ class OnScreenEditActivity : AppCompatActivity() {
binding.enabledCheckbox.setOnClickListener { _ -> binding.enabledCheckbox.setOnClickListener { _ ->
binding.onScreenControllerView.setButtonEnabled(binding.enabledCheckbox.isChecked) binding.onScreenControllerView.setButtonEnabled(binding.enabledCheckbox.isChecked)
} }
binding.toggleModeCheckbox.setOnClickListener { _ ->
binding.onScreenControllerView.setButtonToggleMode(binding.toggleModeCheckbox.isChecked)
}
binding.moveUpButton.setOnClickListener { binding.onScreenControllerView.moveButtonUp() } binding.moveUpButton.setOnClickListener { binding.onScreenControllerView.moveButtonUp() }
binding.moveDownButton.setOnClickListener { binding.onScreenControllerView.moveButtonDown() } binding.moveDownButton.setOnClickListener { binding.onScreenControllerView.moveButtonDown() }
@ -194,6 +197,8 @@ class OnScreenEditActivity : AppCompatActivity() {
*/ */
private fun updateActiveButtonDisplayInfo(button : ConfigurableButton) { private fun updateActiveButtonDisplayInfo(button : ConfigurableButton) {
binding.enabledCheckbox.checkedState = button.config.groupEnabled binding.enabledCheckbox.checkedState = button.config.groupEnabled
binding.toggleModeCheckbox.isEnabled = button.supportsToggleMode()
binding.toggleModeCheckbox.checkedState = button.config.groupToggleMode
currentButtonName = button.buttonId.short ?: "" currentButtonName = button.buttonId.short ?: ""
binding.currentButton.text = getString(R.string.osc_current_button, currentButtonName) binding.currentButton.text = getString(R.string.osc_current_button, currentButtonName)
binding.scaleSlider.slider.value = (button.config.scale - OnScreenConfiguration.MinScale) / (OnScreenConfiguration.MaxScale - OnScreenConfiguration.MinScale) * 100f binding.scaleSlider.slider.value = (button.config.scale - OnScreenConfiguration.MinScale) / (OnScreenConfiguration.MaxScale - OnScreenConfiguration.MinScale) * 100f

View File

@ -68,6 +68,8 @@ class JoystickButton(
var shortDoubleTapped = false var shortDoubleTapped = false
private set private set
override fun supportsToggleMode() : Boolean = false
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) {
@ -78,7 +80,7 @@ class JoystickButton(
innerButton.render(canvas) innerButton.render(canvas)
} }
override fun onFingerDown(x : Float, y : Float) { override fun onFingerDown(x : Float, y : Float) : Boolean {
val relativeX = x / width val relativeX = x / width
val relativeY = (y - heightDiff) / adjustedHeight val relativeY = (y - heightDiff) / adjustedHeight
if (recenterSticks) { if (recenterSticks) {
@ -99,9 +101,11 @@ class JoystickButton(
isPressed = true isPressed = true
} }
fingerDownTime = currentTime fingerDownTime = currentTime
return true
} }
override fun onFingerUp(x : Float, y : Float) { override fun onFingerUp(x : Float, y : Float) : Boolean {
loadConfigValues() loadConfigValues()
innerButton.relativeX = relativeX innerButton.relativeX = relativeX
innerButton.relativeY = relativeY innerButton.relativeY = relativeY
@ -109,6 +113,8 @@ class JoystickButton(
fingerUpTime = SystemClock.elapsedRealtime() fingerUpTime = SystemClock.elapsedRealtime()
shortDoubleTapped = false shortDoubleTapped = false
isPressed = false isPressed = false
return true
} }
fun onFingerMoved(x : Float, y : Float, manualMove : Boolean = true) : PointF { fun onFingerMoved(x : Float, y : Float, manualMove : Boolean = true) : PointF {

View File

@ -137,7 +137,6 @@
android:minHeight="0dp" android:minHeight="0dp"
android:paddingVertical="8dp" android:paddingVertical="8dp"
android:text="@string/osc_toggle_mode" android:text="@string/osc_toggle_mode"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/enabled_checkbox" app:layout_constraintStart_toStartOf="@+id/enabled_checkbox"
app:layout_constraintTop_toBottomOf="@id/enabled_checkbox" app:layout_constraintTop_toBottomOf="@id/enabled_checkbox"
tools:ignore="TouchTargetSizeCheck" /> tools:ignore="TouchTargetSizeCheck" />