diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index f8cb55874..a3311e1a9 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -962,6 +962,7 @@ object NativeLibrary { const val BUTTON_GPIO14 = 782 const val BUTTON_SWAP = 800 const val BUTTON_TURBO = 801 + const val BUTTON_COMBO = 802 } /** diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt index e12d87544..99339a8fc 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt @@ -56,7 +56,8 @@ enum class BooleanSetting( COMPRESS_INSTALLED_CIA_CONTENT(SettingKeys.compress_cia_installs(), Settings.SECTION_STORAGE, false), ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false), APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true), - USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false); + USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false), + ENABLE_COMBO_KEY("enable_combo_key", Settings.SECTION_CONTROLS, true); override var boolean: Boolean = defaultValue diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt index 547a53594..70056503b 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt @@ -115,6 +115,7 @@ class Settings { const val SECTION_STORAGE = "Storage" const val SECTION_MISC = "Miscellaneous" + const val KEY_BUTTON_A = "button_a" const val KEY_BUTTON_B = "button_b" const val KEY_BUTTON_X = "button_x" @@ -130,6 +131,7 @@ class Settings { const val KEY_BUTTON_R = "button_r" const val KEY_BUTTON_ZL = "button_zl" const val KEY_BUTTON_ZR = "button_zr" + const val KEY_BUTTON_COMBO = "button_combo" const val KEY_CIRCLEPAD_AXIS_VERTICAL = "circlepad_axis_vertical" const val KEY_CIRCLEPAD_AXIS_HORIZONTAL = "circlepad_axis_horizontal" const val KEY_CSTICK_AXIS_VERTICAL = "cstick_axis_vertical" @@ -152,7 +154,8 @@ class Settings { KEY_BUTTON_Y, KEY_BUTTON_SELECT, KEY_BUTTON_START, - KEY_BUTTON_HOME + KEY_BUTTON_HOME, + KEY_BUTTON_COMBO ) val buttonTitles = listOf( R.string.button_a, @@ -161,7 +164,8 @@ class Settings { R.string.button_y, R.string.button_select, R.string.button_start, - R.string.button_home + R.string.button_home, + R.string.button_combo ) val circlePadKeys = listOf( KEY_CIRCLEPAD_AXIS_VERTICAL, diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/StringListSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/StringListSetting.kt new file mode 100644 index 000000000..dd12c54dc --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/StringListSetting.kt @@ -0,0 +1,52 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package org.citra.citra_emu.features.settings.model + +enum class StringListSetting( + override val key: String, + override val section: String, + override val defaultValue: List, + val canBeEmpty: Boolean = true +) : AbstractListSetting { + COMBO_KEYS("combo_keys", Settings.SECTION_CONTROLS, listOf("A", "B", "X", "Y", "L", "R", "ZL", "ZR", "Start", "Select")); + + private var backingList: List = defaultValue + private var lastValidList : List = defaultValue + + override var list: List + get() = backingList + set(value) { + if (!canBeEmpty && value.isEmpty()) { + backingList = lastValidList + } else { + backingList = value + lastValidList = value + } + } + + override val valueAsString: String + get() = list.joinToString() + + + 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:List = listOf(); + + + fun from(key: String): StringListSetting? = + values().firstOrNull { it.key == key } + + fun clear() = values().forEach { it.list = it.defaultValue } + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt index 6ec851db1..1b16cb070 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt @@ -125,6 +125,7 @@ class InputBindingSetting( Settings.KEY_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_SELECT Settings.KEY_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_START Settings.KEY_BUTTON_HOME -> NativeLibrary.ButtonType.BUTTON_HOME + Settings.KEY_BUTTON_COMBO -> NativeLibrary.ButtonType.BUTTON_COMBO Settings.KEY_BUTTON_UP -> NativeLibrary.ButtonType.DPAD_UP Settings.KEY_BUTTON_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN Settings.KEY_BUTTON_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT @@ -522,6 +523,7 @@ class InputBindingSetting( NativeLibrary.ButtonType.BUTTON_SELECT -> Settings.KEY_BUTTON_SELECT NativeLibrary.ButtonType.BUTTON_START -> Settings.KEY_BUTTON_START NativeLibrary.ButtonType.BUTTON_HOME -> Settings.KEY_BUTTON_HOME + NativeLibrary.ButtonType.BUTTON_COMBO -> Settings.KEY_BUTTON_COMBO NativeLibrary.ButtonType.DPAD_UP -> Settings.KEY_BUTTON_UP NativeLibrary.ButtonType.DPAD_DOWN -> Settings.KEY_BUTTON_DOWN NativeLibrary.ButtonType.DPAD_LEFT -> Settings.KEY_BUTTON_LEFT diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SettingsItem.kt index 066912dd9..c855d2b03 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SettingsItem.kt @@ -48,5 +48,6 @@ abstract class SettingsItem( const val TYPE_STRING_INPUT = 9 const val TYPE_FLOAT_INPUT = 10 const val TYPE_MULTI_CHOICE = 11 + const val TYPE_STRING_MULTI_CHOICE = 12 } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/StringMultiChoiceSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/StringMultiChoiceSetting.kt new file mode 100644 index 000000000..b6dd0a8a9 --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/StringMultiChoiceSetting.kt @@ -0,0 +1,53 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package org.citra.citra_emu.features.settings.model.view +import org.citra.citra_emu.features.settings.model.AbstractSetting +import org.citra.citra_emu.features.settings.model.StringListSetting +class StringMultiChoiceSetting( + setting: AbstractSetting?, + titleId: Int, + descriptionId: Int, + val choices: Array, + val values: Array?, + val key: String? = null, + private val defaultValue: List? = null, + override var isEnabled: Boolean = true +) : SettingsItem(setting, titleId, descriptionId) { + override val type = TYPE_STRING_MULTI_CHOICE + + fun getValueAt(index: Int): String? { + if (values == null) return null + return if (index >= 0 && index < values.size) { + values[index] + } else { + "" + } + } + val selectedValues: List + get() { + if (setting == null) { + return defaultValue!! + } + try { + val setting = setting as StringListSetting + return setting.list + }catch (_: ClassCastException) { + } + return defaultValue!! + } + + /** + * Write a value to the backing list. If that list was previously null, + * initializes a new one and returns it, so it can be added to the Hashmap. + * + * @param selection New value of the int. + * @return the existing setting with the new value applied. + */ + fun setSelectedValue(selection: List): StringListSetting { + val stringSetting = setting as StringListSetting + stringSetting.list = selection + return stringSetting + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt index 43a1dcbbd..bd89861f6 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt @@ -44,6 +44,7 @@ import org.citra.citra_emu.features.settings.model.FloatSetting import org.citra.citra_emu.features.settings.model.IntListSetting import org.citra.citra_emu.features.settings.model.ScaledFloatSetting import org.citra.citra_emu.features.settings.model.AbstractShortSetting +import org.citra.citra_emu.features.settings.model.Settings import org.citra.citra_emu.features.settings.model.view.DateTimeSetting import org.citra.citra_emu.features.settings.model.view.InputBindingSetting import org.citra.citra_emu.features.settings.model.view.SettingsItem @@ -51,6 +52,7 @@ import org.citra.citra_emu.features.settings.model.view.SingleChoiceSetting import org.citra.citra_emu.features.settings.model.view.MultiChoiceSetting import org.citra.citra_emu.features.settings.model.view.SliderSetting import org.citra.citra_emu.features.settings.model.view.StringInputSetting +import org.citra.citra_emu.features.settings.model.view.StringMultiChoiceSetting import org.citra.citra_emu.features.settings.model.view.StringSingleChoiceSetting import org.citra.citra_emu.features.settings.model.view.SubmenuSetting import org.citra.citra_emu.features.settings.model.view.SwitchSetting @@ -137,6 +139,10 @@ class SettingsAdapter( StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this) } + SettingsItem.TYPE_MULTI_CHOICE, SettingsItem.TYPE_STRING_MULTI_CHOICE -> { + MultiChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this) + } + else -> { // TODO: Create an error view since we can't return null now HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this) @@ -198,9 +204,14 @@ class SettingsAdapter( SettingsItem.TYPE_SINGLE_CHOICE -> { (oldItem as SingleChoiceSetting).isEnabled == (newItem as SingleChoiceSetting).isEnabled } + SettingsItem.TYPE_MULTI_CHOICE -> { (oldItem as MultiChoiceSetting).isEnabled == (newItem as MultiChoiceSetting).isEnabled } + + SettingsItem.TYPE_STRING_MULTI_CHOICE -> { + (oldItem as StringMultiChoiceSetting).isEnabled == (newItem as StringMultiChoiceSetting).isEnabled + } SettingsItem.TYPE_DATETIME_SETTING -> { (oldItem as DateTimeSetting).isEnabled == (newItem as DateTimeSetting).isEnabled @@ -271,6 +282,26 @@ class SettingsAdapter( onMultiChoiceClick(item) } + private fun onStringMultiChoiceClick(item: StringMultiChoiceSetting) { + clickedItem = item + val values: BooleanArray = getSelectionForStringMultiChoiceValue(item); + dialog = MaterialAlertDialogBuilder(context) + .setTitle(item.nameId) + .setMultiChoiceItems(item.choices, values, this) + .setOnDismissListener { + if (clickedPosition != -1) { + notifyItemChanged(clickedPosition) + clickedPosition = -1 + } + } + .show() + } + + fun onStringMultiChoiceClick(item: StringMultiChoiceSetting, position: Int) { + clickedPosition = position + onStringMultiChoiceClick(item) + } + private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) { clickedItem = item dialog = context?.let { @@ -571,15 +602,34 @@ class SettingsAdapter( //onclick for multichoice override fun onClick(dialog: DialogInterface?, which: Int, isChecked: Boolean) { - val mcsetting = clickedItem as? MultiChoiceSetting - mcsetting?.let { - val value = getValueForMultiChoiceSelection(it, which) - if (it.selectedValues.contains(value) != isChecked) { - val setting = it.setSelectedValue((if (isChecked) it.selectedValues + value else it.selectedValues - value).sorted()) - fragmentView?.putSetting(setting) - fragmentView?.onSettingChanged() + when (clickedItem) { + is MultiChoiceSetting -> { + val mcsetting = clickedItem as? MultiChoiceSetting + mcsetting?.let { + val value = getValueForMultiChoiceSelection(it, which) + if (it.selectedValues.contains(value) != isChecked) { + val setting = + it.setSelectedValue((if (isChecked) it.selectedValues + value else it.selectedValues - value).sorted()) + fragmentView?.putSetting(setting) + fragmentView?.onSettingChanged() + } + fragmentView.loadSettingsList() + } + } + + is StringMultiChoiceSetting -> { + val mcsetting = clickedItem as? StringMultiChoiceSetting + mcsetting?.let { + val value = it.getValueAt(which) + if (it.selectedValues.contains(value) != isChecked) { + val setting = + it.setSelectedValue((if (isChecked) it.selectedValues + value.toString() else it.selectedValues - value.toString())) + fragmentView?.putSetting(setting) + fragmentView?.onSettingChanged() + } + fragmentView.loadSettingsList() + } } - fragmentView.loadSettingsList() } } @@ -714,6 +764,7 @@ class SettingsAdapter( } } + private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { val value = item.selectedValue val valuesId = item.valuesId @@ -746,4 +797,27 @@ class SettingsAdapter( } return BooleanArray(1){false}; } + + //TODO: Properly Implement in line with string single choice + private fun getSelectionForStringMultiChoiceValue(item: StringMultiChoiceSetting): BooleanArray { + val values = item.selectedValues; + val available_values = item.values; + val res = BooleanArray(available_values?.size ?: 10){false} // 10 is used because we have 10 reasonable options. + // prob shouldn't hardcode the size value here + + + if (available_values != null) { + for (choice_val in available_values) { + if (values.contains(choice_val)) { + val index = available_values.indexOf(choice_val) + res[index] = true; + } + } + } + return if (res.isNotEmpty()) { + res; + } else { + BooleanArray(1) { false }; + } + } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 25e56517f..ced0ef8a2 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -31,6 +31,7 @@ import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.IntListSetting import org.citra.citra_emu.features.settings.model.ScaledFloatSetting import org.citra.citra_emu.features.settings.model.Settings +import org.citra.citra_emu.features.settings.model.StringListSetting import org.citra.citra_emu.features.settings.model.StringSetting import org.citra.citra_emu.features.settings.model.view.DateTimeSetting import org.citra.citra_emu.features.settings.model.view.HeaderSetting @@ -41,6 +42,7 @@ import org.citra.citra_emu.features.settings.model.view.SettingsItem import org.citra.citra_emu.features.settings.model.view.SingleChoiceSetting import org.citra.citra_emu.features.settings.model.view.SliderSetting import org.citra.citra_emu.features.settings.model.view.StringInputSetting +import org.citra.citra_emu.features.settings.model.view.StringMultiChoiceSetting import org.citra.citra_emu.features.settings.model.view.StringSingleChoiceSetting import org.citra.citra_emu.features.settings.model.view.SubmenuSetting import org.citra.citra_emu.features.settings.model.view.SwitchSetting @@ -778,6 +780,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) private fun addControlsSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_controls)) + sl.apply { add( RunnableSetting( @@ -839,9 +842,24 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) BooleanSetting.USE_ARTIC_BASE_CONTROLLER.defaultValue ) ) + + val buttons = settingsActivity.resources.getStringArray(R.array.n3dsButtons).take(10).toTypedArray() + add( + StringMultiChoiceSetting( + StringListSetting.COMBO_KEYS, + R.string.combo_key_options, + R.string.combo_key_description, + buttons, + buttons, + StringListSetting.COMBO_KEYS.key, + StringListSetting.COMBO_KEYS.defaultValue + ) + ) } } + + private fun getInputObject(key: String): AbstractStringSetting { return object : AbstractStringSetting { override var string: String diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/viewholder/MultiChoiceViewHolder.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/viewholder/MultiChoiceViewHolder.kt index 8493115a4..4e59ed984 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/viewholder/MultiChoiceViewHolder.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/viewholder/MultiChoiceViewHolder.kt @@ -8,6 +8,7 @@ import android.view.View import org.citra.citra_emu.databinding.ListItemSettingBinding import org.citra.citra_emu.features.settings.model.view.SettingsItem import org.citra.citra_emu.features.settings.model.view.MultiChoiceSetting +import org.citra.citra_emu.features.settings.model.view.StringMultiChoiceSetting import org.citra.citra_emu.features.settings.ui.SettingsAdapter class MultiChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : @@ -51,6 +52,16 @@ class MultiChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Settin return resList.joinToString(); } + is StringMultiChoiceSetting -> { + var resList:List = emptyList(); + item.values?.forEachIndexed { i: Int, value: String -> + if (value in item.selectedValues) { + resList = resList + item.choices[i] + } + } + return resList.joinToString() + } + else -> return "" } } @@ -67,6 +78,12 @@ class MultiChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Settin bindingAdapterPosition ) } + if (setting is StringMultiChoiceSetting) { + adapter.onStringMultiChoiceClick( + (setting as StringMultiChoiceSetting), + bindingAdapterPosition + ) + } } override fun onLongClick(clicked: View): Boolean { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.kt index a9e1d4743..2ff8727c6 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.kt @@ -17,6 +17,7 @@ import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.ScaledFloatSetting import org.citra.citra_emu.features.settings.model.SettingSection import org.citra.citra_emu.features.settings.model.Settings.SettingsSectionMap +import org.citra.citra_emu.features.settings.model.StringListSetting import org.citra.citra_emu.features.settings.model.StringSetting import org.citra.citra_emu.features.settings.ui.SettingsActivityView import org.citra.citra_emu.utils.BiMap @@ -256,6 +257,11 @@ object SettingsFile { return stringSetting } + val stringListSetting = StringListSetting.from(key) + if (stringListSetting != null) { + stringListSetting.list = value.split(", ").map { it } + } + val intListSetting = IntListSetting.from(key) if (intListSetting != null) { intListSetting.list = value.split(", ").map { it.toInt() } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt index 785438526..768086141 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt @@ -831,6 +831,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram true } + R.id.menu_emulation_adjust_scale_button_combo -> { + showAdjustScaleDialog("controlScale-" + NativeLibrary.ButtonType.BUTTON_COMBO) + true + } + R.id.menu_emulation_adjust_opacity -> { showAdjustOpacityDialog() true @@ -1048,13 +1053,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram private fun showToggleControlsDialog() { val editor = preferences.edit() - val enabledButtons = BooleanArray(16) + val enabledButtons = BooleanArray(17) enabledButtons.forEachIndexed { i: Int, _: Boolean -> // Buttons that are disabled by default var defaultValue = true when (i) { - // TODO: Remove these magic numbers - 6, 7, 12, 13, 14, 15 -> defaultValue = false + 6, 7, 12, 13, 14, 15, 16 -> defaultValue = false } enabledButtons[i] = preferences.getBoolean("buttonToggle$i", defaultValue) } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt index f7519bb81..84fd0e8d9 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt @@ -26,6 +26,7 @@ import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.R import org.citra.citra_emu.utils.EmulationMenuSettings import org.citra.citra_emu.utils.TurboHelper +import org.citra.citra_emu.utils.ComboHelper import java.lang.NullPointerException import kotlin.math.min @@ -157,6 +158,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex NativeLibrary.onTouchEvent(0f, 0f, false) break // Up and down actions shouldn't loop } + } var anyOverlayStateChanged = false @@ -175,6 +177,9 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex else if (button.id == NativeLibrary.ButtonType.BUTTON_TURBO && button.status == NativeLibrary.ButtonState.PRESSED) { TurboHelper.toggleTurbo(true) } + else if (button.id == NativeLibrary.ButtonType.BUTTON_COMBO) { + ComboHelper.comboActivate(button) + } NativeLibrary.onGamePadEvent( NativeLibrary.TouchScreenDevice, @@ -568,6 +573,18 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex ) ) } + + if (preferences.getBoolean("buttonToggle16", false)) { + overlayButtons.add( + initializeOverlayButton( + context, + R.drawable.button_combo, + R.drawable.button_combo_pressed, + NativeLibrary.ButtonType.BUTTON_COMBO, + orientation + ) + ) + } } fun refreshControls() { @@ -781,6 +798,14 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex NativeLibrary.ButtonType.BUTTON_TURBO.toString() + "-Y", resources.getInteger(R.integer.N3DS_BUTTON_TURBO_Y).toFloat() / 1000 * maxY ) + .putFloat( + NativeLibrary.ButtonType.BUTTON_COMBO.toString() + "-X", + resources.getInteger(R.integer.N3DS_BUTTON_COMBO_X).toFloat() / 1000 * maxX + ) + .putFloat( + NativeLibrary.ButtonType.BUTTON_COMBO.toString() + "-Y", + resources.getInteger(R.integer.N3DS_BUTTON_COMBO_Y).toFloat() / 1000 * maxY + ) .apply() } @@ -932,6 +957,14 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex NativeLibrary.ButtonType.BUTTON_TURBO.toString() + portrait + "-Y", resources.getInteger(R.integer.N3DS_BUTTON_TURBO_PORTRAIT_Y).toFloat() / 1000 * maxY ) + .putFloat( + NativeLibrary.ButtonType.BUTTON_COMBO.toString() + portrait + "-X", + resources.getInteger(R.integer.N3DS_BUTTON_COMBO_PORTRAIT_X).toFloat() / 1000 * maxX + ) + .putFloat( + NativeLibrary.ButtonType.BUTTON_COMBO.toString() + portrait + "-Y", + resources.getInteger(R.integer.N3DS_BUTTON_COMBO_PORTRAIT_Y).toFloat() / 1000 * maxY + ) .apply() } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/ComboHelper.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/ComboHelper.kt new file mode 100644 index 000000000..603fd1796 --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/ComboHelper.kt @@ -0,0 +1,48 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package org.citra.citra_emu.utils + +import org.citra.citra_emu.NativeLibrary +import org.citra.citra_emu.overlay.InputOverlayDrawableButton +import org.citra.citra_emu.features.settings.model.StringListSetting + +object ComboHelper { + + fun getButton(button: String): Int { + when (button) { + "A" -> return NativeLibrary.ButtonType.BUTTON_A + "B" -> return NativeLibrary.ButtonType.BUTTON_B + "X" -> return NativeLibrary.ButtonType.BUTTON_X + "Y" -> return NativeLibrary.ButtonType.BUTTON_Y + "L" -> return NativeLibrary.ButtonType.TRIGGER_L + "R" -> return NativeLibrary.ButtonType.TRIGGER_R + "ZL" -> return NativeLibrary.ButtonType.BUTTON_ZL + "ZR" -> return NativeLibrary.ButtonType.BUTTON_ZR + "START" -> return NativeLibrary.ButtonType.BUTTON_START + "SELECT" -> return NativeLibrary.ButtonType.BUTTON_SELECT + } + return -1 + } + + fun comboActivate(button: InputOverlayDrawableButton) { + val comboArray = StringListSetting.COMBO_KEYS.list + for (selected_button in comboArray) { + val native_button = getButton(selected_button) + if (native_button == -1) + { + val cause = IllegalStateException("Button is not in bindable inputs.") + throw IllegalArgumentException("Input must be non-negative.", cause) + } + else + { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, native_button, button.status) + } + } + } + + + + +} diff --git a/src/android/app/src/main/res/drawable/button_combo.xml b/src/android/app/src/main/res/drawable/button_combo.xml new file mode 100644 index 000000000..fdd9a79a4 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_combo.xml @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/drawable/button_combo_pressed.xml b/src/android/app/src/main/res/drawable/button_combo_pressed.xml new file mode 100644 index 000000000..d8825692d --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_combo_pressed.xml @@ -0,0 +1,28 @@ + + + + diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml index 8bb19ee26..34589ebc3 100644 --- a/src/android/app/src/main/res/menu/menu_overlay_options.xml +++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml @@ -79,6 +79,9 @@ + diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 2a08cd546..d581e4f65 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -181,6 +181,7 @@ @string/button_home @string/button_swap @string/button_turbo + @string/button_combo diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml index 1f19c2373..0c091a48b 100644 --- a/src/android/app/src/main/res/values/integers.xml +++ b/src/android/app/src/main/res/values/integers.xml @@ -35,6 +35,8 @@ 850 630 850 + 740 + 480 810 @@ -69,5 +71,7 @@ 675 453 720 + 445 + 925 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 750677fd5..16cc6e7b6 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -156,6 +156,7 @@ HOME Swap Screens Turbo + Combo Key X Y L @@ -167,6 +168,10 @@ Turbo Speed Turbo Speed Enabled Turbo Speed Disabled + Enable Combo Button + Combo Key Settings + Enable and Change Combo Button Bindings. + Changes whether or not Combo Button can be displayed and used in game. System Files