android: Abstract settings

Previously we could only add settings that would change our ini file. Now we can create abstract settings in our presenter to alter things like shared preferences for theme support!
This commit is contained in:
Charles Lombardo 2023-04-18 03:43:33 -04:00 committed by bunnei
parent 6dfe4240ac
commit c609847e49
24 changed files with 417 additions and 362 deletions

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractBooleanSetting : AbstractSetting {
var boolean: Boolean
}

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractFloatSetting : AbstractSetting {
var float: Float
}

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractIntSetting : AbstractSetting {
var int: Int
}

View file

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractSetting {
val key: String?
val section: String?
val isRuntimeEditable: Boolean
val valueAsString: String
}

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
interface AbstractStringSetting : AbstractSetting {
var string: String
}

View file

@ -3,10 +3,33 @@
package org.yuzu.yuzu_emu.features.settings.model
class BooleanSetting(
key: String,
section: String,
var value: Boolean
) : Setting(key, section) {
override val valueAsString get() = if (value) "True" else "False"
enum class BooleanSetting(
override val key: String,
override val section: String,
val defaultValue: Boolean
) : AbstractBooleanSetting {
// No boolean settings currently exist
EMPTY_SETTING("", "", false);
override var boolean: Boolean = defaultValue
override val valueAsString: String
get() = boolean.toString()
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = emptyList<BooleanSetting>()
fun from(key: String): BooleanSetting? =
BooleanSetting.values().firstOrNull { it.key == key }
}
}

View file

@ -3,10 +3,32 @@
package org.yuzu.yuzu_emu.features.settings.model
class FloatSetting(
key: String,
section: String,
var value: Float
) : Setting(key, section) {
override val valueAsString get() = value.toString()
enum class FloatSetting(
override val key: String,
override val section: String,
val defaultValue: Float
) : AbstractFloatSetting {
// No float settings currently exist
EMPTY_SETTING("", "", 0f);
override var float: Float = defaultValue
override val valueAsString: String
get() = float.toString()
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key }
}
}

View file

@ -3,10 +3,121 @@
package org.yuzu.yuzu_emu.features.settings.model
class IntSetting(
key: String,
section: String,
var value: Int
) : Setting(key, section) {
override val valueAsString get() = value.toString()
enum class IntSetting(
override val key: String,
override val section: String,
val defaultValue: Int
) : AbstractIntSetting {
RENDERER_USE_SPEED_LIMIT(
"use_speed_limit",
Settings.SECTION_RENDERER,
1
),
USE_DOCKED_MODE(
"use_docked_mode",
Settings.SECTION_SYSTEM,
0
),
RENDERER_USE_DISK_SHADER_CACHE(
"use_disk_shader_cache",
Settings.SECTION_RENDERER,
1
),
RENDERER_FORCE_MAX_CLOCK(
"force_max_clock",
Settings.SECTION_RENDERER,
1
),
RENDERER_ASYNCHRONOUS_SHADERS(
"use_asynchronous_shaders",
Settings.SECTION_RENDERER,
0
),
RENDERER_DEBUG(
"debug",
Settings.SECTION_RENDERER,
0
),
RENDERER_SPEED_LIMIT(
"speed_limit",
Settings.SECTION_RENDERER,
100
),
CPU_ACCURACY(
"cpu_accuracy",
Settings.SECTION_CPU,
0
),
REGION_INDEX(
"region_index",
Settings.SECTION_SYSTEM,
-1
),
LANGUAGE_INDEX(
"language_index",
Settings.SECTION_SYSTEM,
1
),
RENDERER_BACKEND(
"backend",
Settings.SECTION_RENDERER,
1
),
RENDERER_ACCURACY(
"gpu_accuracy",
Settings.SECTION_RENDERER,
0
),
RENDERER_RESOLUTION(
"resolution_setup",
Settings.SECTION_RENDERER,
2
),
RENDERER_SCALING_FILTER(
"scaling_filter",
Settings.SECTION_RENDERER,
1
),
RENDERER_ANTI_ALIASING(
"anti_aliasing",
Settings.SECTION_RENDERER,
0
),
RENDERER_ASPECT_RATIO(
"aspect_ratio",
Settings.SECTION_RENDERER,
0
),
AUDIO_VOLUME(
"volume",
Settings.SECTION_AUDIO,
100
);
override var int: Int = defaultValue
override val valueAsString: String
get() = int.toString()
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
RENDERER_USE_DISK_SHADER_CACHE,
RENDERER_ASYNCHRONOUS_SHADERS,
RENDERER_DEBUG,
RENDERER_BACKEND,
RENDERER_RESOLUTION
)
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
}
}

View file

@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
/**
* Abstraction for a setting item as read from / written to yuzu's configuration ini files.
* These files generally consist of a key/value pair, though the type of value is ambiguous and
* must be inferred at read-time. The type of value determines which child of this class is used
* to represent the Setting.
*/
abstract class Setting(
/**
* @return The identifier used to write this setting to the ini file.
*/
val key: String,
/**
* @return The name of the header under which this Setting should be written in the ini file.
*/
val section: String
) {
/**
* @return A representation of this Setting's backing value converted to a String (e.g. for serialization).
*/
abstract val valueAsString: String
}

View file

@ -8,15 +8,15 @@ package org.yuzu.yuzu_emu.features.settings.model
* internally stored as a HashMap.
*/
class SettingSection(val name: String) {
val settings = HashMap<String, Setting>()
val settings = HashMap<String, AbstractSetting>()
/**
* Convenience method; inserts a value directly into the backing HashMap.
*
* @param setting The Setting to be inserted.
*/
fun putSetting(setting: Setting) {
settings[setting.key] = setting
fun putSetting(setting: AbstractSetting) {
settings[setting.key!!] = setting
}
/**
@ -25,7 +25,7 @@ class SettingSection(val name: String) {
* @param key Used to retrieve the Setting.
* @return A Setting object (you should probably cast this before using)
*/
fun getSetting(key: String): Setting? {
fun getSetting(key: String): AbstractSetting? {
return settings[key]
}

View file

@ -3,10 +3,32 @@
package org.yuzu.yuzu_emu.features.settings.model
class StringSetting(
key: String,
section: String,
var value: String
) : Setting(key, section) {
override val valueAsString get() = value
enum class StringSetting(
override val key: String,
override val section: String,
defaultValue: String
) : AbstractStringSetting {
// No string settings currently exist
EMPTY_SETTING("", "", "");
override var string: String = defaultValue
override val valueAsString: String
get() = string
override val isRuntimeEditable: Boolean
get() {
for (setting in NOT_RUNTIME_EDITABLE) {
if (setting == this) {
return false
}
}
return true
}
companion object {
private val NOT_RUNTIME_EDITABLE = emptyList<StringSetting>()
fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
}
}

View file

@ -3,36 +3,30 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
class DateTimeSetting(
key: String,
section: String,
val key: String? = null,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
private val defaultValue: String,
setting: Setting
) : SettingsItem(key, section, setting, titleId, descriptionId) {
private val defaultValue: String? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_DATETIME_SETTING
val value: String
get() = if (setting != null) {
val setting = setting as StringSetting
setting.value
setting.string
} else {
defaultValue
defaultValue!!
}
fun setSelectedValue(datetime: String): StringSetting? {
return if (setting == null) {
val newSetting = StringSetting(key!!, section!!, datetime)
setting = newSetting
newSetting
} else {
val newSetting = setting as StringSetting
newSetting.value = datetime
null
}
fun setSelectedValue(datetime: String): AbstractStringSetting {
val stringSetting = setting as AbstractStringSetting
stringSetting.string = datetime
return stringSetting
}
}

View file

@ -3,13 +3,12 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class HeaderSetting(
key: String?,
setting: Setting?,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int?
) : SettingsItem(key, null, setting, titleId, descriptionId) {
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_HEADER
}

View file

@ -3,19 +3,17 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
/**
* ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
* Each one corresponds to a [Setting] object, so this class's subclasses
* Each one corresponds to a [AbstractSetting] object, so this class's subclasses
* should vaguely correspond to those subclasses. There are a few with multiple analogues
* and a few with none (Headers, for example, do not correspond to anything in the ini
* file.)
*/
abstract class SettingsItem(
val key: String?,
val section: String?,
var setting: Setting?,
var setting: AbstractSetting?,
val nameId: Int,
val descriptionId: Int?
) {

View file

@ -3,27 +3,26 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Setting
class SingleChoiceSetting(
key: String,
section: String,
setting: Setting?,
setting: AbstractIntSetting?,
titleId: Int,
descriptionId: Int,
val choicesId: Int,
val valuesId: Int,
private val defaultValue: Int,
) : SettingsItem(key, section, setting, titleId, descriptionId) {
val key: String? = null,
val defaultValue: Int? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SINGLE_CHOICE
val selectedValue: Int
get() = if (setting != null) {
val setting = setting as IntSetting
setting.value
setting.int
} else {
defaultValue
defaultValue!!
}
/**
@ -31,17 +30,11 @@ class SingleChoiceSetting(
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: Int): IntSetting? {
return if (setting == null) {
val newSetting = IntSetting(key!!, section!!, selection)
setting = newSetting
newSetting
} else {
val newSetting = setting as IntSetting
newSetting.value = selection
null
}
fun setSelectedValue(selection: Int): AbstractIntSetting {
val intSetting = setting as AbstractIntSetting
intSetting.int = selection
return intSetting
}
}

View file

@ -3,31 +3,32 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.utils.Log
import kotlin.math.roundToInt
class SliderSetting(
key: String,
section: String,
setting: Setting?,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val min: Int,
val max: Int,
val units: String,
val defaultValue: Int,
) : SettingsItem(key, section, setting, titleId, descriptionId) {
val key: String? = null,
val defaultValue: Int? = null,
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER
val selectedValue: Int
get() {
val setting = setting ?: return defaultValue
val setting = setting ?: return defaultValue!!
return when (setting) {
is IntSetting -> setting.value
is FloatSetting -> setting.value.roundToInt()
is IntSetting -> setting.int
is FloatSetting -> setting.float.roundToInt()
else -> {
Log.error("[SliderSetting] Error casting setting type.")
-1
@ -40,18 +41,12 @@ class SliderSetting(
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: Int): IntSetting? {
return if (setting == null) {
val newSetting = IntSetting(key!!, section!!, selection)
setting = newSetting
newSetting
} else {
val newSetting = setting as IntSetting
newSetting.value = selection
null
}
fun setSelectedValue(selection: Int): AbstractIntSetting {
val intSetting = setting as AbstractIntSetting
intSetting.int = selection
return intSetting
}
/**
@ -59,17 +54,11 @@ class SliderSetting(
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the float.
* @return null if overwritten successfully otherwise; a newly created FloatSetting.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: Float): FloatSetting? {
return if (setting == null) {
val newSetting = FloatSetting(key!!, section!!, selection)
setting = newSetting
newSetting
} else {
val newSetting = setting as FloatSetting
newSetting.value = selection
null
}
fun setSelectedValue(selection: Float): AbstractFloatSetting {
val floatSetting = setting as AbstractFloatSetting
floatSetting.float = selection
return floatSetting
}
}

View file

@ -3,19 +3,19 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
class StringSingleChoiceSetting(
key: String,
section: String,
setting: Setting?,
val key: String? = null,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val choicesId: Array<String>,
private val valuesId: Array<String>?,
private val defaultValue: String
) : SettingsItem(key, section, setting, titleId, descriptionId) {
private val defaultValue: String? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_SINGLE_CHOICE
fun getValueAt(index: Int): String? {
@ -28,9 +28,9 @@ class StringSingleChoiceSetting(
val selectedValue: String
get() = if (setting != null) {
val setting = setting as StringSetting
setting.value
setting.string
} else {
defaultValue
defaultValue!!
}
val selectValueIndex: Int
get() {
@ -48,17 +48,11 @@ class StringSingleChoiceSetting(
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
* @return the existing setting with the new value applied.
*/
fun setSelectedValue(selection: String?): StringSetting? {
return if (setting == null) {
val newSetting = StringSetting(key!!, section!!, selection!!)
setting = newSetting
newSetting
} else {
val newSetting = setting as StringSetting
newSetting.value = selection!!
null
}
fun setSelectedValue(selection: String): AbstractStringSetting {
val stringSetting = setting as AbstractStringSetting
stringSetting.string = selection
return stringSetting
}
}

View file

@ -3,16 +3,13 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class SubmenuSetting(
key: String?,
setting: Setting?,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val menuKey: String
) : SettingsItem(
key, null, setting, titleId, descriptionId
) {
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SUBMENU
}

View file

@ -3,67 +3,42 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragmentView
import org.yuzu.yuzu_emu.utils.Log
class SwitchSetting : SettingsItem {
class SwitchSetting(
setting: AbstractSetting,
titleId: Int,
descriptionId: Int,
val key: String? = null,
val defaultValue: Boolean? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SWITCH
private var defaultValue: Boolean
private var showPerformanceWarning: Boolean
private var fragmentView: SettingsFragmentView? = null
constructor(
key: String,
section: String,
setting: Setting?,
titleId: Int,
descriptionId: Int,
defaultValue: Boolean
) : super(key, section, setting, titleId, descriptionId) {
this.defaultValue = defaultValue
showPerformanceWarning = false
}
constructor(
key: String,
section: String,
titleId: Int,
descriptionId: Int,
defaultValue: Boolean,
setting: Setting,
show_performance_warning: Boolean,
view: SettingsFragmentView
) : super(key, section, setting, titleId, descriptionId) {
this.defaultValue = defaultValue
fragmentView = view
showPerformanceWarning = show_performance_warning
}
val isChecked: Boolean
get() {
if (setting == null) {
return defaultValue
return defaultValue!!
}
// Try integer setting
try {
val setting = setting as IntSetting
return setting.value == 1
val setting = setting as AbstractIntSetting
return setting.int == 1
} catch (_: ClassCastException) {
}
// Try boolean setting
try {
val setting = setting as BooleanSetting
return setting.value
val setting = setting as AbstractBooleanSetting
return setting.boolean
} catch (_: ClassCastException) {
}
return defaultValue
return defaultValue!!
}
/**
@ -71,24 +46,20 @@ class SwitchSetting : SettingsItem {
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param checked Pretty self explanatory.
* @return null if overwritten successfully; otherwise, a newly created BooleanSetting.
* @return the existing setting with the new value applied.
*/
fun setChecked(checked: Boolean): IntSetting? {
// Show a performance warning if the setting has been disabled
if (showPerformanceWarning && !checked) {
fragmentView!!.showToastMessage(
YuzuApplication.appContext.getString(R.string.performance_warning), true
)
fun setChecked(checked: Boolean): AbstractSetting {
// Try integer setting
try {
val setting = setting as AbstractIntSetting
setting.int = if (checked) 1 else 0
return setting
} catch (_: ClassCastException) {
}
return if (setting == null) {
val newSetting = IntSetting(key!!, section!!, if (checked) 1 else 0)
setting = newSetting
newSetting
} else {
val newSetting = setting as IntSetting
newSetting.value = if (checked) 1 else 0
null
}
// Try boolean setting
val setting = setting as AbstractBooleanSetting
setting.boolean = checked
return setting
}
}

View file

@ -101,9 +101,7 @@ class SettingsAdapter(
fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
val setting = item.setChecked(checked)
if (setting != null) {
fragmentView.putSetting(setting)
}
fragmentView.onSettingChanged()
}
@ -209,7 +207,7 @@ class SettingsAdapter(
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
sliderBinding.slider.value = item.defaultValue.toFloat()
sliderBinding.slider.value = item.defaultValue!!.toFloat()
onClick(dialog, which)
}
.show()
@ -230,19 +228,15 @@ class SettingsAdapter(
// Get the backing Setting, which may be null (if for example it was missing from the file)
val setting = scSetting.setSelectedValue(value)
if (setting != null) {
fragmentView.putSetting(setting)
}
closeDialog()
}
is StringSingleChoiceSetting -> {
val scSetting = clickedItem as StringSingleChoiceSetting
val value = scSetting.getValueAt(which)
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
val setting = scSetting.setSelectedValue(value)
if (setting != null) {
val setting = scSetting.setSelectedValue(value!!)
fragmentView.putSetting(setting)
}
closeDialog()
}
is SliderSetting -> {
@ -253,15 +247,11 @@ class SettingsAdapter(
if (sliderSetting.setting is FloatSetting) {
val value = sliderProgress.toFloat()
val setting = sliderSetting.setSelectedValue(value)
if (setting != null) {
fragmentView.putSetting(setting)
}
} else {
val setting = sliderSetting.setSelectedValue(sliderProgress)
if (setting != null) {
fragmentView.putSetting(setting)
}
}
closeDialog()
}
}

View file

@ -15,8 +15,7 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
class SettingsFragment : Fragment(), SettingsFragmentView {
@ -91,7 +90,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
activityView!!.showToastMessage(message!!, is_long)
}
override fun putSetting(setting: Setting) {
override fun putSetting(setting: AbstractSetting) {
fragmentPresenter.putSetting(setting)
}

View file

@ -6,7 +6,9 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.text.TextUtils
import androidx.appcompat.app.AppCompatActivity
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
@ -28,8 +30,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
loadSettingsList()
}
fun putSetting(setting: Setting) {
settings.getSection(setting.section)!!.putSetting(setting)
fun putSetting(setting: AbstractSetting) {
val section = settings.getSection(setting.section!!)!!
if (section.getSetting(setting.key!!) == null) {
section.putSetting(setting)
}
}
fun loadSettingsList() {
@ -60,7 +65,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
sl.apply {
add(
SubmenuSetting(
null,
null,
R.string.preferences_general,
0,
@ -69,7 +73,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SubmenuSetting(
null,
null,
R.string.preferences_system,
0,
@ -78,7 +81,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SubmenuSetting(
null,
null,
R.string.preferences_graphics,
0,
@ -87,7 +89,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SubmenuSetting(
null,
null,
R.string.preferences_audio,
0,
@ -99,45 +100,36 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setTitle(R.string.preferences_general)
val rendererSection = settings.getSection(Settings.SECTION_RENDERER)
val frameLimitEnable =
rendererSection!!.getSetting(SettingsFile.KEY_RENDERER_USE_SPEED_LIMIT)
val frameLimitValue = rendererSection.getSetting(SettingsFile.KEY_RENDERER_SPEED_LIMIT)
val cpuSection = settings.getSection(Settings.SECTION_CPU)
val cpuAccuracy = cpuSection!!.getSetting(SettingsFile.KEY_CPU_ACCURACY)
sl.apply {
add(
SwitchSetting(
SettingsFile.KEY_RENDERER_USE_SPEED_LIMIT,
Settings.SECTION_RENDERER,
frameLimitEnable,
IntSetting.RENDERER_USE_SPEED_LIMIT,
R.string.frame_limit_enable,
R.string.frame_limit_enable_description,
IntSetting.RENDERER_USE_SPEED_LIMIT.key,
true
)
)
add(
SliderSetting(
SettingsFile.KEY_RENDERER_SPEED_LIMIT,
Settings.SECTION_RENDERER,
frameLimitValue,
IntSetting.RENDERER_SPEED_LIMIT,
R.string.frame_limit_slider,
R.string.frame_limit_slider_description,
1,
200,
"%",
IntSetting.RENDERER_SPEED_LIMIT.key,
100
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_CPU_ACCURACY,
Settings.SECTION_CPU,
cpuAccuracy,
IntSetting.CPU_ACCURACY,
R.string.cpu_accuracy,
0,
R.array.cpuAccuracyNames,
R.array.cpuAccuracyValues,
IntSetting.CPU_ACCURACY.key,
0
)
)
@ -146,42 +138,35 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setTitle(R.string.preferences_system)
val systemSection = settings.getSection(Settings.SECTION_SYSTEM)
val dockedMode = systemSection!!.getSetting(SettingsFile.KEY_USE_DOCKED_MODE)
val region = systemSection.getSetting(SettingsFile.KEY_REGION_INDEX)
val language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE_INDEX)
sl.apply {
add(
SwitchSetting(
SettingsFile.KEY_USE_DOCKED_MODE,
Settings.SECTION_SYSTEM,
dockedMode,
IntSetting.USE_DOCKED_MODE,
R.string.use_docked_mode,
R.string.use_docked_mode_description,
false,
IntSetting.USE_DOCKED_MODE.key,
false
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_REGION_INDEX,
Settings.SECTION_SYSTEM,
region,
IntSetting.REGION_INDEX,
R.string.emulated_region,
0,
R.array.regionNames,
R.array.regionValues,
IntSetting.REGION_INDEX.key,
-1
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_LANGUAGE_INDEX,
Settings.SECTION_SYSTEM,
language,
IntSetting.LANGUAGE_INDEX,
R.string.emulated_language,
0,
R.array.languageNames,
R.array.languageValues,
IntSetting.LANGUAGE_INDEX.key,
1
)
)
@ -190,133 +175,106 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setTitle(R.string.preferences_graphics)
val rendererSection = settings.getSection(Settings.SECTION_RENDERER)
val rendererBackend = rendererSection!!.getSetting(SettingsFile.KEY_RENDERER_BACKEND)
val rendererAccuracy = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ACCURACY)
val rendererResolution = rendererSection.getSetting(SettingsFile.KEY_RENDERER_RESOLUTION)
val rendererScalingFilter =
rendererSection.getSetting(SettingsFile.KEY_RENDERER_SCALING_FILTER)
val rendererAntiAliasing =
rendererSection.getSetting(SettingsFile.KEY_RENDERER_ANTI_ALIASING)
val rendererAspectRatio =
rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASPECT_RATIO)
val rendererUseDiskShaderCache =
rendererSection.getSetting(SettingsFile.KEY_RENDERER_USE_DISK_SHADER_CACHE)
val rendererForceMaxClocks =
rendererSection.getSetting(SettingsFile.KEY_RENDERER_FORCE_MAX_CLOCK)
val rendererAsynchronousShaders =
rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS)
val rendererDebug = rendererSection.getSetting(SettingsFile.KEY_RENDERER_DEBUG)
sl.apply {
add(
SingleChoiceSetting(
SettingsFile.KEY_RENDERER_BACKEND,
Settings.SECTION_RENDERER,
rendererBackend,
IntSetting.RENDERER_BACKEND,
R.string.renderer_api,
0,
R.array.rendererApiNames,
R.array.rendererApiValues,
IntSetting.RENDERER_BACKEND.key,
1
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_RENDERER_ACCURACY,
Settings.SECTION_RENDERER,
rendererAccuracy,
IntSetting.RENDERER_ACCURACY,
R.string.renderer_accuracy,
0,
R.array.rendererAccuracyNames,
R.array.rendererAccuracyValues,
IntSetting.RENDERER_ACCURACY.key,
0
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_RENDERER_RESOLUTION,
Settings.SECTION_RENDERER,
rendererResolution,
IntSetting.RENDERER_RESOLUTION,
R.string.renderer_resolution,
0,
R.array.rendererResolutionNames,
R.array.rendererResolutionValues,
IntSetting.RENDERER_RESOLUTION.key,
2
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_RENDERER_SCALING_FILTER,
Settings.SECTION_RENDERER,
rendererScalingFilter,
IntSetting.RENDERER_SCALING_FILTER,
R.string.renderer_scaling_filter,
0,
R.array.rendererScalingFilterNames,
R.array.rendererScalingFilterValues,
IntSetting.RENDERER_SCALING_FILTER.key,
1
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_RENDERER_ANTI_ALIASING,
Settings.SECTION_RENDERER,
rendererAntiAliasing,
IntSetting.RENDERER_ANTI_ALIASING,
R.string.renderer_anti_aliasing,
0,
R.array.rendererAntiAliasingNames,
R.array.rendererAntiAliasingValues,
IntSetting.RENDERER_ANTI_ALIASING.key,
0
)
)
add(
SingleChoiceSetting(
SettingsFile.KEY_RENDERER_ASPECT_RATIO,
Settings.SECTION_RENDERER,
rendererAspectRatio,
IntSetting.RENDERER_ASPECT_RATIO,
R.string.renderer_aspect_ratio,
0,
R.array.rendererAspectRatioNames,
R.array.rendererAspectRatioValues,
IntSetting.RENDERER_ASPECT_RATIO.key,
0
)
)
add(
SwitchSetting(
SettingsFile.KEY_RENDERER_USE_DISK_SHADER_CACHE,
Settings.SECTION_RENDERER,
rendererUseDiskShaderCache,
IntSetting.RENDERER_USE_DISK_SHADER_CACHE,
R.string.use_disk_shader_cache,
R.string.use_disk_shader_cache_description,
IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
true
)
)
add(
SwitchSetting(
SettingsFile.KEY_RENDERER_FORCE_MAX_CLOCK,
Settings.SECTION_RENDERER,
rendererForceMaxClocks,
IntSetting.RENDERER_FORCE_MAX_CLOCK,
R.string.renderer_force_max_clock,
R.string.renderer_force_max_clock_description,
IntSetting.RENDERER_FORCE_MAX_CLOCK.key,
true
)
)
add(
SwitchSetting(
SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS,
Settings.SECTION_RENDERER,
rendererAsynchronousShaders,
IntSetting.RENDERER_ASYNCHRONOUS_SHADERS,
R.string.renderer_asynchronous_shaders,
R.string.renderer_asynchronous_shaders_description,
IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
false
)
)
add(
SwitchSetting(
SettingsFile.KEY_RENDERER_DEBUG,
Settings.SECTION_RENDERER,
rendererDebug,
IntSetting.RENDERER_DEBUG,
R.string.renderer_debug,
R.string.renderer_debug_description,
IntSetting.RENDERER_DEBUG.key,
false
)
)
@ -325,18 +283,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setTitle(R.string.preferences_audio)
val audioSection = settings.getSection(Settings.SECTION_AUDIO)
val audioVolume = audioSection!!.getSetting(SettingsFile.KEY_AUDIO_VOLUME)
sl.add(
SliderSetting(
SettingsFile.KEY_AUDIO_VOLUME,
Settings.SECTION_AUDIO,
audioVolume,
IntSetting.AUDIO_VOLUME,
R.string.audio_volume,
R.string.audio_volume_description,
0,
100,
"%",
IntSetting.AUDIO_VOLUME.key,
100
)
)

View file

@ -3,7 +3,7 @@
package org.yuzu.yuzu_emu.features.settings.ui
import org.yuzu.yuzu_emu.features.settings.model.Setting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
/**
@ -49,7 +49,7 @@ interface SettingsFragmentView {
*
* @param setting The (possibly previously missing) new setting.
*/
fun putSetting(setting: Setting)
fun putSetting(setting: AbstractSetting)
/**
* Have the fragment tell the containing Activity that a setting was modified.

View file

@ -21,32 +21,7 @@ import java.util.*
*/
object SettingsFile {
const val FILE_NAME_CONFIG = "config"
const val KEY_DESIGN = "design"
// CPU
const val KEY_CPU_ACCURACY = "cpu_accuracy"
// System
const val KEY_USE_DOCKED_MODE = "use_docked_mode"
const val KEY_REGION_INDEX = "region_index"
const val KEY_LANGUAGE_INDEX = "language_index"
const val KEY_RENDERER_BACKEND = "backend"
// Renderer
const val KEY_RENDERER_RESOLUTION = "resolution_setup"
const val KEY_RENDERER_SCALING_FILTER = "scaling_filter"
const val KEY_RENDERER_ANTI_ALIASING = "anti_aliasing"
const val KEY_RENDERER_ASPECT_RATIO = "aspect_ratio"
const val KEY_RENDERER_ACCURACY = "gpu_accuracy"
const val KEY_RENDERER_USE_DISK_SHADER_CACHE = "use_disk_shader_cache"
const val KEY_RENDERER_ASYNCHRONOUS_SHADERS = "use_asynchronous_shaders"
const val KEY_RENDERER_FORCE_MAX_CLOCK = "force_max_clock"
const val KEY_RENDERER_USE_SPEED_LIMIT = "use_speed_limit"
const val KEY_RENDERER_DEBUG = "debug"
const val KEY_RENDERER_SPEED_LIMIT = "speed_limit"
// Audio
const val KEY_AUDIO_VOLUME = "volume"
private val sectionsMap = BiMap<String?, String?>()
/**
@ -75,7 +50,7 @@ object SettingsFile {
current = sectionFromLine(line!!, isCustomGame)
sections[current.name] = current
} else if (current != null) {
val setting = settingFromLine(current, line!!)
val setting = settingFromLine(line!!)
if (setting != null) {
current.putSetting(setting)
}
@ -201,11 +176,10 @@ object SettingsFile {
* For a line of text, determines what type of data is being represented, and returns
* a Setting object containing this data.
*
* @param current The section currently being parsed by the consuming method.
* @param line The line of text being parsed.
* @return A typed Setting containing the key/value contained in the line.
*/
private fun settingFromLine(current: SettingSection, line: String): Setting? {
private fun settingFromLine(line: String): AbstractSetting? {
val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (splitLine.size != 2) {
Log.warning("Skipping invalid config line \"$line\"")
@ -217,17 +191,25 @@ object SettingsFile {
Log.warning("Skipping null value in config line \"$line\"")
return null
}
try {
val valueAsInt = value.toInt()
return IntSetting(key, current.name, valueAsInt)
} catch (_: NumberFormatException) {
val booleanSetting = BooleanSetting.from(key)
if (booleanSetting != null) {
booleanSetting.boolean = value.toBoolean()
return booleanSetting
}
try {
val valueAsFloat = value.toFloat()
return FloatSetting(key, current.name, valueAsFloat)
} catch (_: NumberFormatException) {
val intSetting = IntSetting.from(key)
if (intSetting != null) {
intSetting.int = value.toInt()
return intSetting
}
return StringSetting(key, current.name, value)
val floatSetting = FloatSetting.from(key)
if (floatSetting != null) {
floatSetting.float = value.toFloat()
return floatSetting
}
return StringSetting.from(key)
}
/**