mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Android: Show input indicators in controller settings
This commit is contained in:
@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
package org.dolphinemu.dolphinemu.features.input.ui
|
package org.dolphinemu.dolphinemu.features.input.ui
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding
|
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding
|
||||||
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
|
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
|
||||||
import org.dolphinemu.dolphinemu.features.input.model.CoreDevice
|
import org.dolphinemu.dolphinemu.features.input.model.CoreDevice
|
||||||
import org.dolphinemu.dolphinemu.utils.LifecycleViewHolder
|
import org.dolphinemu.dolphinemu.utils.LifecycleViewHolder
|
||||||
import org.dolphinemu.dolphinemu.utils.Log
|
import java.text.DecimalFormat
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
class AdvancedMappingControlViewHolder(
|
class AdvancedMappingControlViewHolder(
|
||||||
private val binding: ListItemAdvancedMappingControlBinding,
|
private val binding: ListItemAdvancedMappingControlBinding,
|
||||||
@ -19,12 +21,16 @@ class AdvancedMappingControlViewHolder(
|
|||||||
|
|
||||||
private lateinit var control: CoreDevice.Control
|
private lateinit var control: CoreDevice.Control
|
||||||
|
|
||||||
|
private var previousState = Float.POSITIVE_INFINITY
|
||||||
|
|
||||||
init {
|
init {
|
||||||
binding.root.setOnClickListener { onClickCallback.accept(control.getName()) }
|
binding.root.setOnClickListener { onClickCallback.accept(control.getName()) }
|
||||||
if (isInput) {
|
if (isInput) {
|
||||||
ControllerInterface.inputStateChanged.observe(this) {
|
ControllerInterface.inputStateChanged.observe(this) {
|
||||||
updateInputValue()
|
updateInputValue()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
binding.layoutState.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +47,23 @@ class AdvancedMappingControlViewHolder(
|
|||||||
throw IllegalStateException("AdvancedMappingControlViewHolder leak")
|
throw IllegalStateException("AdvancedMappingControlViewHolder leak")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
var state = control.getState().toFloat()
|
||||||
Log.info("AdvancedMappingControlViewHolder: Value of " + control.getName() + " is " +
|
if (abs(state - previousState) >= stateUpdateThreshold) {
|
||||||
control.getState())
|
previousState = state
|
||||||
|
|
||||||
|
// Don't print a minus sign for signed zeroes
|
||||||
|
if (state == -0.0f)
|
||||||
|
state = 0.0f
|
||||||
|
|
||||||
|
binding.textState.setText(stateFormat.format(state))
|
||||||
|
binding.controlStateBar.state = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val stateFormat = DecimalFormat("#.####")
|
||||||
|
|
||||||
|
// For performance, require state to change by a certain threshold before we update the UI
|
||||||
|
private val stateUpdateThreshold = 0.00005f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.dolphinemu.dolphinemu.features.input.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ViewControlStateBarHorizontalBinding
|
||||||
|
|
||||||
|
class ControlStateBarHorizontal @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val binding =
|
||||||
|
ViewControlStateBarHorizontalBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
|
private val impl = ControlStateBarImpl(binding.viewFilled, binding.viewUnfilled)
|
||||||
|
|
||||||
|
var state: Float
|
||||||
|
get() = impl.state
|
||||||
|
set(value) {
|
||||||
|
impl.state = value
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package org.dolphinemu.dolphinemu.features.input.ui
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
|
||||||
|
class ControlStateBarImpl(private val filledView: View, private val unfilledView: View) {
|
||||||
|
private var _state = 0.0f
|
||||||
|
|
||||||
|
var state: Float
|
||||||
|
get() = _state
|
||||||
|
set(value) {
|
||||||
|
val clampedState = MathUtils.clamp(value, 0.0f, 1.0f)
|
||||||
|
if (clampedState != _state) {
|
||||||
|
setLinearLayoutWeight(filledView, clampedState)
|
||||||
|
setLinearLayoutWeight(unfilledView, 1.0f - clampedState)
|
||||||
|
}
|
||||||
|
_state = clampedState
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun setLinearLayoutWeight(view: View, weight: Float) {
|
||||||
|
val layoutParams = view.layoutParams as LinearLayout.LayoutParams
|
||||||
|
layoutParams.weight = weight
|
||||||
|
view.layoutParams = layoutParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package org.dolphinemu.dolphinemu.features.input.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ViewControlStateBarVerticalBinding
|
||||||
|
|
||||||
|
class ControlStateBarVertical @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
// Setting this in the XML file has no effect for some reason
|
||||||
|
orientation = VERTICAL
|
||||||
|
}
|
||||||
|
|
||||||
|
private val binding =
|
||||||
|
ViewControlStateBarVerticalBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
|
private val impl = ControlStateBarImpl(binding.viewFilled, binding.viewUnfilled)
|
||||||
|
|
||||||
|
var state: Float
|
||||||
|
get() = impl.state
|
||||||
|
set(value) {
|
||||||
|
impl.state = value
|
||||||
|
}
|
||||||
|
}
|
@ -10,14 +10,17 @@ import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSe
|
|||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter
|
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder
|
||||||
import org.dolphinemu.dolphinemu.utils.Log
|
import kotlin.math.abs
|
||||||
|
|
||||||
class InputMappingControlSettingViewHolder(
|
class InputMappingControlSettingViewHolder(
|
||||||
private val binding: ListItemMappingBinding,
|
private val binding: ListItemMappingBinding,
|
||||||
adapter: SettingsAdapter
|
adapter: SettingsAdapter
|
||||||
) : SettingViewHolder(binding.getRoot(), adapter) {
|
) : SettingViewHolder(binding.getRoot(), adapter) {
|
||||||
|
|
||||||
lateinit var setting: InputMappingControlSetting
|
lateinit var setting: InputMappingControlSetting
|
||||||
|
|
||||||
|
private var previousState = Float.POSITIVE_INFINITY
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ControllerInterface.inputStateChanged.observe(this) {
|
ControllerInterface.inputStateChanged.observe(this) {
|
||||||
updateInputValue()
|
updateInputValue()
|
||||||
@ -34,6 +37,10 @@ class InputMappingControlSettingViewHolder(
|
|||||||
binding.textSettingDescription.text = setting.value
|
binding.textSettingDescription.text = setting.value
|
||||||
binding.buttonAdvancedSettings.setOnClickListener { clicked: View -> onLongClick(clicked) }
|
binding.buttonAdvancedSettings.setOnClickListener { clicked: View -> onLongClick(clicked) }
|
||||||
|
|
||||||
|
if (!setting.isInput) {
|
||||||
|
binding.controlStateBar.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
setStyle(binding.textSettingName, setting)
|
setStyle(binding.textSettingName, setting)
|
||||||
updateInputValue()
|
updateInputValue()
|
||||||
}
|
}
|
||||||
@ -69,9 +76,16 @@ class InputMappingControlSettingViewHolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (setting.isInput) {
|
if (setting.isInput) {
|
||||||
// TODO
|
val state = setting.state.toFloat()
|
||||||
Log.info("InputMappingControlSettingViewHolder: Value of " + setting.name + " is " +
|
if (abs(state - previousState) >= stateUpdateThreshold) {
|
||||||
setting.state)
|
previousState = state
|
||||||
|
binding.controlStateBar.state = state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// For performance, require state to change by a certain threshold before we update the UI
|
||||||
|
private val stateUpdateThreshold = 0.01f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
android:layout_marginTop="@dimen/spacing_large"
|
android:layout_marginTop="@dimen/spacing_large"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
android:layout_toStartOf="@id/control_state_bar"
|
||||||
tools:text="Setting Name" />
|
tools:text="Setting Name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -30,16 +30,23 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignStart="@+id/text_setting_name"
|
android:layout_alignStart="@id/text_setting_name"
|
||||||
android:layout_below="@+id/text_setting_name"
|
android:layout_below="@id/text_setting_name"
|
||||||
android:layout_marginBottom="@dimen/spacing_large"
|
android:layout_marginBottom="@dimen/spacing_large"
|
||||||
android:layout_marginEnd="@dimen/spacing_large"
|
android:layout_marginEnd="@dimen/spacing_large"
|
||||||
android:layout_marginStart="@dimen/spacing_large"
|
android:layout_marginStart="@dimen/spacing_large"
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
android:layout_toStartOf="@id/control_state_bar"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
tools:text="@string/overclock_enable_description" />
|
tools:text="@string/overclock_enable_description" />
|
||||||
|
|
||||||
|
<org.dolphinemu.dolphinemu.features.input.ui.ControlStateBarVertical
|
||||||
|
android:id="@+id/control_state_bar"
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@id/button_advanced_settings" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_advanced_settings"
|
android:id="@+id/button_advanced_settings"
|
||||||
style="?attr/materialIconButtonStyle"
|
style="?attr/materialIconButtonStyle"
|
||||||
|
@ -1,27 +1,48 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:minHeight="54dp"
|
android:minHeight="54dp"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:clickable="true">
|
android:clickable="true">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
style="@style/TextAppearance.MaterialComponents.Headline5"
|
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
tools:text="Button A"
|
tools:text="Button A"
|
||||||
android:layout_alignParentEnd="true"
|
android:padding="@dimen/spacing_large"
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_marginStart="@dimen/spacing_large"
|
|
||||||
android:layout_marginEnd="@dimen/spacing_large"
|
|
||||||
android:layout_marginTop="@dimen/spacing_large"
|
|
||||||
android:id="@+id/text_name"
|
android:id="@+id/text_name"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:id="@+id/layout_state">
|
||||||
|
|
||||||
|
<org.dolphinemu.dolphinemu.features.input.ui.ControlStateBarHorizontal
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/control_state_bar" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal|center_vertical"
|
||||||
|
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
|
tools:text="0.1234"
|
||||||
|
android:padding="@dimen/spacing_large"
|
||||||
|
android:id="@+id/text_state"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
android:layout_marginTop="@dimen/spacing_large"
|
android:layout_marginTop="@dimen/spacing_large"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
android:layout_toStartOf="@id/control_state_bar"
|
||||||
tools:text="Setting Name" />
|
tools:text="Setting Name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -30,16 +30,23 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignStart="@+id/text_setting_name"
|
android:layout_alignStart="@id/text_setting_name"
|
||||||
android:layout_below="@+id/text_setting_name"
|
android:layout_below="@id/text_setting_name"
|
||||||
android:layout_marginBottom="@dimen/spacing_large"
|
android:layout_marginBottom="@dimen/spacing_large"
|
||||||
android:layout_marginEnd="@dimen/spacing_large"
|
android:layout_marginEnd="@dimen/spacing_large"
|
||||||
android:layout_marginStart="@dimen/spacing_large"
|
android:layout_marginStart="@dimen/spacing_large"
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
android:layout_toStartOf="@id/control_state_bar"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
tools:text="@string/overclock_enable_description" />
|
tools:text="@string/overclock_enable_description" />
|
||||||
|
|
||||||
|
<org.dolphinemu.dolphinemu.features.input.ui.ControlStateBarVertical
|
||||||
|
android:id="@+id/control_state_bar"
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@id/button_advanced_settings" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_advanced_settings"
|
android:id="@+id/button_advanced_settings"
|
||||||
style="?attr/materialIconButtonStyle"
|
style="?attr/materialIconButtonStyle"
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:parentTag="LinearLayout">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:id="@+id/view_filled"
|
||||||
|
android:background="@color/dolphin_errorContainer" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:id="@+id/view_unfilled" />
|
||||||
|
|
||||||
|
</merge>
|
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:parentTag="LinearLayout">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:id="@+id/view_unfilled" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:id="@+id/view_filled"
|
||||||
|
android:background="@color/dolphin_error" />
|
||||||
|
|
||||||
|
</merge>
|
Reference in New Issue
Block a user