mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-12-23 15:42:39 -06:00
Added "Small Screen Position" feature
* error checking for layout value from older config * rename enum and update aspect ratio code * rewrite LargeFrameLayout to support multiple positions * add settings for smallscreenposition, fix minsize function * fixed framebuffer from res scale (screenshots) * add desktop UI for small screen position * small screen position submenu on desktop * fix int-float conversion warning * rename Above and Below to hopefully fix linux issue * Add Small Screen Position Setting to android settings menu * fix sliders to work with floats, mostly * fix android slider textinput ui * change None enums in settings and cam_params * Apply clang-format-18 * SettingsAdapter.kt: Make more null pointer exception resistant * Updated license headers * Code formatting nitpicks * fix bug in main.ui that was hiding menu * replace default layout with a special call to LargeFrame (like SideBySide does) * fix bug when "large screen" is actually narrower * edit documentation for LargeScreenLayout * update PortraitTopFullFrameLayout to use LargeFrameLayout * fix unary minus on unsigned int bug * Applied formatting correction * Added `const`s where appropriate * android: Add mention of the bottom-right small screen position being the default * review fixes + more constants * refactor all Upright calculations to a reverseLayout method, simplifying code and reducing bugs * Removed stray extra newline * SettingsAdapter.kt: Fixed some strange indentation * Removed unnecessary `if` in favour of direct value usage --------- Co-authored-by: Reg Tiangha <rtiangha@users.noreply.github.com> Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
This commit is contained in:
parent
21aec70f67
commit
ff98896782
29 changed files with 780 additions and 454 deletions
|
@ -21,6 +21,23 @@ enum class ScreenLayout(val int: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SmallScreenPosition(val int: Int) {
|
||||||
|
TOP_RIGHT(0),
|
||||||
|
MIDDLE_RIGHT(1),
|
||||||
|
BOTTOM_RIGHT(2),
|
||||||
|
TOP_LEFT(3),
|
||||||
|
MIDDLE_LEFT(4),
|
||||||
|
BOTTOM_LEFT(5),
|
||||||
|
ABOVE(6),
|
||||||
|
BELOW(7);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun from(int: Int): SmallScreenPosition {
|
||||||
|
return entries.firstOrNull { it.int == int } ?: TOP_RIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class PortraitScreenLayout(val int: Int) {
|
enum class PortraitScreenLayout(val int: Int) {
|
||||||
// These must match what is defined in src/common/settings.h
|
// These must match what is defined in src/common/settings.h
|
||||||
TOP_FULL_WIDTH(0),
|
TOP_FULL_WIDTH(0),
|
||||||
|
@ -28,7 +45,7 @@ enum class PortraitScreenLayout(val int: Int) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): PortraitScreenLayout {
|
fun from(int: Int): PortraitScreenLayout {
|
||||||
return entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH;
|
return entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ enum class FloatSetting(
|
||||||
override val section: String,
|
override val section: String,
|
||||||
override val defaultValue: Float
|
override val defaultValue: Float
|
||||||
) : AbstractFloatSetting {
|
) : AbstractFloatSetting {
|
||||||
// There are no float settings currently
|
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
|
||||||
EMPTY_SETTING("", "", 0.0f);
|
EMPTY_SETTING("", "", 0.0f);
|
||||||
|
|
||||||
override var float: Float = defaultValue
|
override var float: Float = defaultValue
|
||||||
|
|
|
@ -23,6 +23,7 @@ enum class IntSetting(
|
||||||
CARDBOARD_X_SHIFT("cardboard_x_shift", Settings.SECTION_LAYOUT, 0),
|
CARDBOARD_X_SHIFT("cardboard_x_shift", Settings.SECTION_LAYOUT, 0),
|
||||||
CARDBOARD_Y_SHIFT("cardboard_y_shift", Settings.SECTION_LAYOUT, 0),
|
CARDBOARD_Y_SHIFT("cardboard_y_shift", Settings.SECTION_LAYOUT, 0),
|
||||||
SCREEN_LAYOUT("layout_option", Settings.SECTION_LAYOUT, 0),
|
SCREEN_LAYOUT("layout_option", Settings.SECTION_LAYOUT, 0),
|
||||||
|
SMALL_SCREEN_POSITION("small_screen_position",Settings.SECTION_LAYOUT,0),
|
||||||
LANDSCAPE_TOP_X("custom_top_x",Settings.SECTION_LAYOUT,0),
|
LANDSCAPE_TOP_X("custom_top_x",Settings.SECTION_LAYOUT,0),
|
||||||
LANDSCAPE_TOP_Y("custom_top_y",Settings.SECTION_LAYOUT,0),
|
LANDSCAPE_TOP_Y("custom_top_y",Settings.SECTION_LAYOUT,0),
|
||||||
LANDSCAPE_TOP_WIDTH("custom_top_width",Settings.SECTION_LAYOUT,800),
|
LANDSCAPE_TOP_WIDTH("custom_top_width",Settings.SECTION_LAYOUT,800),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -39,5 +39,6 @@ abstract class SettingsItem(
|
||||||
const val TYPE_RUNNABLE = 7
|
const val TYPE_RUNNABLE = 7
|
||||||
const val TYPE_INPUT_BINDING = 8
|
const val TYPE_INPUT_BINDING = 8
|
||||||
const val TYPE_STRING_INPUT = 9
|
const val TYPE_STRING_INPUT = 9
|
||||||
|
const val TYPE_FLOAT_INPUT = 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -23,17 +23,16 @@ class SliderSetting(
|
||||||
val defaultValue: Float? = null
|
val defaultValue: Float? = null
|
||||||
) : SettingsItem(setting, titleId, descriptionId) {
|
) : SettingsItem(setting, titleId, descriptionId) {
|
||||||
override val type = TYPE_SLIDER
|
override val type = TYPE_SLIDER
|
||||||
|
val selectedFloat: Float
|
||||||
val selectedValue: Int
|
|
||||||
get() {
|
get() {
|
||||||
val setting = setting ?: return defaultValue!!.toInt()
|
val setting = setting ?: return defaultValue!!.toFloat()
|
||||||
return when (setting) {
|
return when (setting) {
|
||||||
is AbstractIntSetting -> setting.int
|
is AbstractIntSetting -> setting.int.toFloat()
|
||||||
is FloatSetting -> setting.float.roundToInt()
|
is FloatSetting -> setting.float
|
||||||
is ScaledFloatSetting -> setting.float.roundToInt()
|
is ScaledFloatSetting -> setting.float
|
||||||
else -> {
|
else -> {
|
||||||
Log.error("[SliderSetting] Error casting setting type.")
|
Log.error("[SliderSetting] Error casting setting type.")
|
||||||
-1
|
-1f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.icu.util.Calendar
|
||||||
import android.icu.util.TimeZone
|
import android.icu.util.TimeZone
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
|
import android.text.InputType
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -68,6 +69,7 @@ import io.github.lime3ds.android.utils.SystemSaveGame
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
import java.lang.NumberFormatException
|
import java.lang.NumberFormatException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class SettingsAdapter(
|
class SettingsAdapter(
|
||||||
private val fragmentView: SettingsFragmentView,
|
private val fragmentView: SettingsFragmentView,
|
||||||
|
@ -77,7 +79,7 @@ class SettingsAdapter(
|
||||||
private var clickedItem: SettingsItem? = null
|
private var clickedItem: SettingsItem? = null
|
||||||
private var clickedPosition: Int
|
private var clickedPosition: Int
|
||||||
private var dialog: AlertDialog? = null
|
private var dialog: AlertDialog? = null
|
||||||
private var sliderProgress = 0
|
private var sliderProgress = 0f
|
||||||
private var textSliderValue: TextInputEditText? = null
|
private var textSliderValue: TextInputEditText? = null
|
||||||
private var textInputLayout: TextInputLayout? = null
|
private var textInputLayout: TextInputLayout? = null
|
||||||
private var textInputValue: String = ""
|
private var textInputValue: String = ""
|
||||||
|
@ -136,27 +138,23 @@ class SettingsAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
|
||||||
holder.bind(getItem(position))
|
getItem(position)?.let { holder.bind(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getItem(position: Int): SettingsItem {
|
private fun getItem(position: Int): SettingsItem? {
|
||||||
return settings!![position]
|
return settings?.get(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return if (settings != null) {
|
return settings?.size ?: 0
|
||||||
settings!!.size
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return getItem(position).type
|
return getItem(position)?.type ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSettingsList(settings: ArrayList<SettingsItem>?) {
|
fun setSettingsList(settings: ArrayList<SettingsItem>?) {
|
||||||
this.settings = settings
|
this.settings = settings ?: arrayListOf()
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,10 +180,12 @@ class SettingsAdapter(
|
||||||
|
|
||||||
private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) {
|
private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) {
|
||||||
clickedItem = item
|
clickedItem = item
|
||||||
dialog = MaterialAlertDialogBuilder(context)
|
dialog = context?.let {
|
||||||
.setTitle(item.nameId)
|
MaterialAlertDialogBuilder(it)
|
||||||
.setSingleChoiceItems(item.choices, item.selectValueIndex, this)
|
.setTitle(item.nameId)
|
||||||
.show()
|
.setSingleChoiceItems(item.choices, item.selectValueIndex, this)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
|
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
|
||||||
|
@ -231,10 +231,10 @@ class SettingsAdapter(
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
datePicker.addOnPositiveButtonClickListener {
|
datePicker.addOnPositiveButtonClickListener {
|
||||||
timePicker.show(
|
val activity = fragmentView.activityView as? AppCompatActivity
|
||||||
(fragmentView.activityView as AppCompatActivity).supportFragmentManager,
|
activity?.supportFragmentManager?.let { fragmentManager ->
|
||||||
"TimePicker"
|
timePicker.show(fragmentManager, "TimePicker")
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
timePicker.addOnPositiveButtonClickListener {
|
timePicker.addOnPositiveButtonClickListener {
|
||||||
var epochTime: Long = datePicker.selection!! / 1000
|
var epochTime: Long = datePicker.selection!! / 1000
|
||||||
|
@ -258,38 +258,62 @@ class SettingsAdapter(
|
||||||
fun onSliderClick(item: SliderSetting, position: Int) {
|
fun onSliderClick(item: SliderSetting, position: Int) {
|
||||||
clickedItem = item
|
clickedItem = item
|
||||||
clickedPosition = position
|
clickedPosition = position
|
||||||
sliderProgress = item.selectedValue
|
sliderProgress = (item.selectedFloat * 100f).roundToInt() / 100f
|
||||||
|
|
||||||
|
|
||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
val sliderBinding = DialogSliderBinding.inflate(inflater)
|
val sliderBinding = DialogSliderBinding.inflate(inflater)
|
||||||
textInputLayout = sliderBinding.textInput
|
textInputLayout = sliderBinding.textInput
|
||||||
textSliderValue = sliderBinding.textValue
|
textSliderValue = sliderBinding.textValue
|
||||||
textSliderValue!!.setText(sliderProgress.toString())
|
if (item.setting is FloatSetting) {
|
||||||
textInputLayout!!.suffixText = item.units
|
textSliderValue?.let {
|
||||||
|
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
it.setText(sliderProgress.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
textSliderValue?.setText(sliderProgress.roundToInt().toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
textInputLayout?.suffixText = item.units
|
||||||
|
|
||||||
sliderBinding.slider.apply {
|
sliderBinding.slider.apply {
|
||||||
valueFrom = item.min.toFloat()
|
valueFrom = item.min.toFloat()
|
||||||
valueTo = item.max.toFloat()
|
valueTo = item.max.toFloat()
|
||||||
value = sliderProgress.toFloat()
|
value = sliderProgress
|
||||||
textSliderValue!!.addTextChangedListener( object : TextWatcher {
|
textSliderValue?.addTextChangedListener(object : TextWatcher {
|
||||||
override fun afterTextChanged(s: Editable) {
|
override fun afterTextChanged(s: Editable) {
|
||||||
val textValue = s.toString().toIntOrNull();
|
var textValue = s.toString().toFloatOrNull();
|
||||||
if (textValue == null || textValue < valueFrom || textValue > valueTo) {
|
if (item.setting !is FloatSetting) {
|
||||||
textInputLayout!!.error ="Inappropriate value"
|
textValue = textValue?.roundToInt()?.toFloat();
|
||||||
} else {
|
|
||||||
textInputLayout!!.error = null
|
|
||||||
value = textValue.toFloat();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
if (textValue == null || textValue < valueFrom || textValue > valueTo) {
|
||||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
textInputLayout?.error = "Inappropriate value"
|
||||||
})
|
} else {
|
||||||
|
textInputLayout?.error = null
|
||||||
|
value = textValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||||
|
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||||
|
})
|
||||||
|
|
||||||
addOnChangeListener { _: Slider, value: Float, _: Boolean ->
|
addOnChangeListener { _: Slider, value: Float, _: Boolean ->
|
||||||
sliderProgress = value.toInt()
|
sliderProgress = (value * 100).roundToInt().toFloat() / 100f
|
||||||
if (textSliderValue!!.text.toString() != value.toInt().toString()) {
|
var sliderString = sliderProgress.toString()
|
||||||
textSliderValue!!.setText(value.toInt().toString())
|
if (item.setting !is FloatSetting) {
|
||||||
textSliderValue!!.setSelection(textSliderValue!!.length())
|
sliderString = sliderProgress.roundToInt().toString()
|
||||||
|
if (textSliderValue?.text.toString() != sliderString) {
|
||||||
|
textSliderValue?.setText(sliderString)
|
||||||
|
textSliderValue?.setSelection(textSliderValue?.length() ?: 0 )
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val currentText = textSliderValue?.text.toString()
|
||||||
|
val currentTextValue = currentText.toFloat()
|
||||||
|
if (currentTextValue != sliderProgress) {
|
||||||
|
textSliderValue?.setText(sliderString)
|
||||||
|
textSliderValue?.setSelection(textSliderValue?.length() ?: 0 )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,14 +324,14 @@ class SettingsAdapter(
|
||||||
.setPositiveButton(android.R.string.ok, this)
|
.setPositiveButton(android.R.string.ok, this)
|
||||||
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
|
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
|
||||||
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
|
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
|
||||||
sliderBinding.slider.value = when (item.setting) {
|
sliderBinding.slider?.value = when (item.setting) {
|
||||||
is ScaledFloatSetting -> {
|
is ScaledFloatSetting -> {
|
||||||
val scaledSetting = item.setting as ScaledFloatSetting
|
val scaledSetting = item.setting as ScaledFloatSetting
|
||||||
scaledSetting.defaultValue * scaledSetting.scale
|
scaledSetting.defaultValue * scaledSetting.scale
|
||||||
}
|
}
|
||||||
|
|
||||||
is FloatSetting -> (item.setting as FloatSetting).defaultValue
|
is FloatSetting -> (item.setting as FloatSetting).defaultValue
|
||||||
else -> item.defaultValue!!
|
else -> item.defaultValue ?: 0f
|
||||||
}
|
}
|
||||||
onClick(dialog, which)
|
onClick(dialog, which)
|
||||||
}
|
}
|
||||||
|
@ -358,85 +382,89 @@ class SettingsAdapter(
|
||||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||||
when (clickedItem) {
|
when (clickedItem) {
|
||||||
is SingleChoiceSetting -> {
|
is SingleChoiceSetting -> {
|
||||||
val scSetting = clickedItem as SingleChoiceSetting
|
val scSetting = clickedItem as? SingleChoiceSetting
|
||||||
val setting = when (scSetting.setting) {
|
scSetting?.let {
|
||||||
is AbstractIntSetting -> {
|
val setting = when (it.setting) {
|
||||||
val value = getValueForSingleChoiceSelection(scSetting, which)
|
is AbstractIntSetting -> {
|
||||||
if (scSetting.selectedValue != value) {
|
val value = getValueForSingleChoiceSelection(it, which)
|
||||||
fragmentView.onSettingChanged()
|
if (it.selectedValue != value) {
|
||||||
|
fragmentView?.onSettingChanged()
|
||||||
|
}
|
||||||
|
it.setSelectedValue(value)
|
||||||
}
|
}
|
||||||
scSetting.setSelectedValue(value)
|
is AbstractShortSetting -> {
|
||||||
}
|
val value = getValueForSingleChoiceSelection(it, which).toShort()
|
||||||
|
if (it.selectedValue.toShort() != value) {
|
||||||
is AbstractShortSetting -> {
|
fragmentView?.onSettingChanged()
|
||||||
val value = getValueForSingleChoiceSelection(scSetting, which).toShort()
|
}
|
||||||
if (scSetting.selectedValue.toShort() != value) {
|
it.setSelectedValue(value)
|
||||||
fragmentView.onSettingChanged()
|
|
||||||
}
|
}
|
||||||
scSetting.setSelectedValue(value)
|
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
|
||||||
}
|
}
|
||||||
|
fragmentView?.putSetting(setting)
|
||||||
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
fragmentView.putSetting(setting)
|
|
||||||
closeDialog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is StringSingleChoiceSetting -> {
|
is StringSingleChoiceSetting -> {
|
||||||
val scSetting = clickedItem as StringSingleChoiceSetting
|
val scSetting = clickedItem as? StringSingleChoiceSetting
|
||||||
val setting = when (scSetting.setting) {
|
scSetting?.let {
|
||||||
is AbstractStringSetting -> {
|
val setting = when (it.setting) {
|
||||||
val value = scSetting.getValueAt(which)
|
is AbstractStringSetting -> {
|
||||||
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
val value = it.getValueAt(which)
|
||||||
scSetting.setSelectedValue(value!!)
|
if (it.selectedValue != value) fragmentView?.onSettingChanged()
|
||||||
|
it.setSelectedValue(value ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
is AbstractShortSetting -> {
|
||||||
|
if (it.selectValueIndex != which) fragmentView?.onSettingChanged()
|
||||||
|
it.setSelectedValue(it.getValueAt(which)?.toShort() ?: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw IllegalStateException("Unrecognized type used for StringSingleChoiceSetting!")
|
||||||
}
|
}
|
||||||
|
|
||||||
is AbstractShortSetting -> {
|
fragmentView?.putSetting(setting)
|
||||||
if (scSetting.selectValueIndex != which) fragmentView.onSettingChanged()
|
closeDialog()
|
||||||
scSetting.setSelectedValue(scSetting.getValueAt(which)?.toShort() ?: 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> throw IllegalStateException("Unrecognized type used for StringSingleChoiceSetting!")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragmentView.putSetting(setting)
|
|
||||||
closeDialog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is SliderSetting -> {
|
is SliderSetting -> {
|
||||||
val sliderSetting = clickedItem as SliderSetting
|
val sliderSetting = clickedItem as? SliderSetting
|
||||||
if (sliderSetting.selectedValue != sliderProgress) {
|
sliderSetting?.let {
|
||||||
fragmentView.onSettingChanged()
|
val sliderval = (it.selectedFloat * 100).roundToInt().toFloat() / 100
|
||||||
}
|
if (sliderval != sliderProgress) {
|
||||||
when (sliderSetting.setting) {
|
fragmentView?.onSettingChanged()
|
||||||
is FloatSetting,
|
|
||||||
is ScaledFloatSetting -> {
|
|
||||||
val value = sliderProgress.toFloat()
|
|
||||||
val setting = sliderSetting.setSelectedValue(value)
|
|
||||||
fragmentView.putSetting(setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val setting = sliderSetting.setSelectedValue(sliderProgress)
|
|
||||||
fragmentView.putSetting(setting)
|
|
||||||
}
|
}
|
||||||
|
when (it.setting) {
|
||||||
|
is AbstractIntSetting -> {
|
||||||
|
val value = sliderProgress.roundToInt()
|
||||||
|
val setting = it.setSelectedValue(value)
|
||||||
|
fragmentView?.putSetting(setting)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val setting = it.setSelectedValue(sliderProgress)
|
||||||
|
fragmentView?.putSetting(setting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeDialog()
|
||||||
}
|
}
|
||||||
closeDialog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is StringInputSetting -> {
|
is StringInputSetting -> {
|
||||||
val inputSetting = clickedItem as StringInputSetting
|
val inputSetting = clickedItem as? StringInputSetting
|
||||||
if (inputSetting.selectedValue != textInputValue) {
|
inputSetting?.let {
|
||||||
fragmentView.onSettingChanged()
|
if (it.selectedValue != textInputValue) {
|
||||||
}
|
fragmentView?.onSettingChanged()
|
||||||
val setting = inputSetting.setSelectedValue(textInputValue)
|
}
|
||||||
fragmentView.putSetting(setting)
|
val setting = it.setSelectedValue(textInputValue ?: "")
|
||||||
closeDialog()
|
fragmentView?.putSetting(setting)
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clickedItem = null
|
clickedItem = null
|
||||||
sliderProgress = -1
|
sliderProgress = -1f
|
||||||
textInputValue = ""
|
textInputValue = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +501,7 @@ class SettingsAdapter(
|
||||||
R.string.setting_not_editable_description
|
R.string.setting_not_editable_description
|
||||||
).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG)
|
).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onClickRegenerateConsoleId() {
|
fun onClickRegenerateConsoleId() {
|
||||||
MaterialAlertDialogBuilder(context)
|
MaterialAlertDialogBuilder(context)
|
||||||
.setTitle(R.string.regenerate_console_id)
|
.setTitle(R.string.regenerate_console_id)
|
||||||
|
|
|
@ -15,6 +15,8 @@ import android.text.TextUtils
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import io.github.lime3ds.android.LimeApplication
|
import io.github.lime3ds.android.LimeApplication
|
||||||
import io.github.lime3ds.android.R
|
import io.github.lime3ds.android.R
|
||||||
|
import io.github.lime3ds.android.display.PortraitScreenLayout
|
||||||
|
import io.github.lime3ds.android.display.ScreenLayout
|
||||||
import io.github.lime3ds.android.features.settings.model.AbstractBooleanSetting
|
import io.github.lime3ds.android.features.settings.model.AbstractBooleanSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.AbstractIntSetting
|
import io.github.lime3ds.android.features.settings.model.AbstractIntSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.AbstractSetting
|
import io.github.lime3ds.android.features.settings.model.AbstractSetting
|
||||||
|
@ -25,6 +27,7 @@ import io.github.lime3ds.android.features.settings.model.ScaledFloatSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.Settings
|
import io.github.lime3ds.android.features.settings.model.Settings
|
||||||
import io.github.lime3ds.android.features.settings.model.StringSetting
|
import io.github.lime3ds.android.features.settings.model.StringSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.AbstractShortSetting
|
import io.github.lime3ds.android.features.settings.model.AbstractShortSetting
|
||||||
|
import io.github.lime3ds.android.features.settings.model.FloatSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.view.DateTimeSetting
|
import io.github.lime3ds.android.features.settings.model.view.DateTimeSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.view.HeaderSetting
|
import io.github.lime3ds.android.features.settings.model.view.HeaderSetting
|
||||||
import io.github.lime3ds.android.features.settings.model.view.InputBindingSetting
|
import io.github.lime3ds.android.features.settings.model.view.InputBindingSetting
|
||||||
|
@ -938,6 +941,29 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||||
IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue
|
IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
SingleChoiceSetting(
|
||||||
|
IntSetting.SMALL_SCREEN_POSITION,
|
||||||
|
R.string.emulation_small_screen_position,
|
||||||
|
R.string.small_screen_position_description,
|
||||||
|
R.array.smallScreenPositions,
|
||||||
|
R.array.smallScreenPositionValues,
|
||||||
|
IntSetting.SMALL_SCREEN_POSITION.key,
|
||||||
|
IntSetting.SMALL_SCREEN_POSITION.defaultValue
|
||||||
|
)
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
SliderSetting(
|
||||||
|
FloatSetting.LARGE_SCREEN_PROPORTION,
|
||||||
|
R.string.large_screen_proportion,
|
||||||
|
R.string.large_screen_proportion_description,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
"",
|
||||||
|
FloatSetting.LARGE_SCREEN_PROPORTION.key,
|
||||||
|
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
|
||||||
|
)
|
||||||
|
)
|
||||||
add(
|
add(
|
||||||
SubmenuSetting(
|
SubmenuSetting(
|
||||||
R.string.emulation_landscape_custom_layout,
|
R.string.emulation_landscape_custom_layout,
|
||||||
|
@ -954,8 +980,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||||
Settings.SECTION_CUSTOM_PORTRAIT
|
Settings.SECTION_CUSTOM_PORTRAIT
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,10 +182,11 @@ void Config::ReadValues() {
|
||||||
layoutInt = static_cast<int>(Settings::LayoutOption::LargeScreen);
|
layoutInt = static_cast<int>(Settings::LayoutOption::LargeScreen);
|
||||||
}
|
}
|
||||||
Settings::values.layout_option = static_cast<Settings::LayoutOption>(layoutInt);
|
Settings::values.layout_option = static_cast<Settings::LayoutOption>(layoutInt);
|
||||||
|
|
||||||
Settings::values.large_screen_proportion =
|
Settings::values.large_screen_proportion =
|
||||||
static_cast<float>(sdl2_config->GetReal("Layout", "large_screen_proportion", 2.25));
|
static_cast<float>(sdl2_config->GetReal("Layout", "large_screen_proportion", 2.25));
|
||||||
|
Settings::values.small_screen_position = static_cast<Settings::SmallScreenPosition>(
|
||||||
|
sdl2_config->GetInteger("Layout", "small_screen_position",
|
||||||
|
static_cast<int>(Settings::SmallScreenPosition::TopRight)));
|
||||||
ReadSetting("Layout", Settings::values.custom_top_x);
|
ReadSetting("Layout", Settings::values.custom_top_x);
|
||||||
ReadSetting("Layout", Settings::values.custom_top_y);
|
ReadSetting("Layout", Settings::values.custom_top_y);
|
||||||
ReadSetting("Layout", Settings::values.custom_top_width);
|
ReadSetting("Layout", Settings::values.custom_top_width);
|
||||||
|
|
|
@ -180,15 +180,26 @@ filter_mode =
|
||||||
|
|
||||||
[Layout]
|
[Layout]
|
||||||
# Layout for the screen inside the render window, landscape mode
|
# Layout for the screen inside the render window, landscape mode
|
||||||
# 0: Top/Bottom *currently unsupported on android*
|
# 0: Original (screens vertically aligned)
|
||||||
# 1: Single Screen Only,
|
# 1: Single Screen Only,
|
||||||
# 2: *currently unsupported on android*
|
# 2: Large Screen (Default on android)
|
||||||
# 3: Side by Side
|
# 3: Side by Side
|
||||||
# 4: Hybrid
|
# 4: Hybrid
|
||||||
# 5: Custom Layout
|
# 5: Custom Layout
|
||||||
# 6: (default) Large screen / small screen
|
|
||||||
layout_option =
|
layout_option =
|
||||||
|
|
||||||
|
# Large Screen Proportion - Relative size of large:small in large screen mode
|
||||||
|
# Default value is 2.25
|
||||||
|
large_screen_proportion =
|
||||||
|
|
||||||
|
# Small Screen Position - where is the small screen relative to the large
|
||||||
|
# Default value is 0
|
||||||
|
# 0: Top Right 1: Middle Right 2: Bottom Right
|
||||||
|
# 3: Top Left 4: Middle left 5: Bottom Left
|
||||||
|
# 6: Above the large screen 7: Below the large screen
|
||||||
|
small_screen_position =
|
||||||
|
|
||||||
|
|
||||||
# Screen placement when using Custom layout option
|
# Screen placement when using Custom layout option
|
||||||
# 0x, 0y is the top left corner of the render window.
|
# 0x, 0y is the top left corner of the render window.
|
||||||
# suggested aspect ratio for top screen is 5:3
|
# suggested aspect ratio for top screen is 5:3
|
||||||
|
|
|
@ -39,6 +39,28 @@
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
|
<string-array name="smallScreenPositions">
|
||||||
|
<item>@string/small_screen_position_top_right</item>
|
||||||
|
<item>@string/small_screen_position_middle_right</item>
|
||||||
|
<item>@string/small_screen_position_bottom_right</item>
|
||||||
|
<item>@string/small_screen_position_top_left</item>
|
||||||
|
<item>@string/small_screen_position_middle_left</item>
|
||||||
|
<item>@string/small_screen_position_bottom_left</item>
|
||||||
|
<item>@string/small_screen_position_above</item>
|
||||||
|
<item>@string/small_screen_position_below</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<integer-array name="smallScreenPositionValues">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
<item>4</item>
|
||||||
|
<item>5</item>
|
||||||
|
<item>6</item>
|
||||||
|
<item>7</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
<string-array name="regionNames">
|
<string-array name="regionNames">
|
||||||
<item>@string/auto_select</item>
|
<item>@string/auto_select</item>
|
||||||
<item>@string/system_region_jpn</item>
|
<item>@string/system_region_jpn</item>
|
||||||
|
|
|
@ -382,6 +382,18 @@
|
||||||
<string name="emulation_screen_layout_original">Original</string>
|
<string name="emulation_screen_layout_original">Original</string>
|
||||||
<string name="emulation_portrait_layout_top_full">Default</string>
|
<string name="emulation_portrait_layout_top_full">Default</string>
|
||||||
<string name="emulation_screen_layout_custom">Custom Layout</string>
|
<string name="emulation_screen_layout_custom">Custom Layout</string>
|
||||||
|
<string name="emulation_small_screen_position">Small Screen Position</string>
|
||||||
|
<string name="small_screen_position_description">Where should the small screen appear relative to the large one in Large Screen Layout?</string>
|
||||||
|
<string name="small_screen_position_top_right">Top Right</string>
|
||||||
|
<string name="small_screen_position_middle_right">Middle Right</string>
|
||||||
|
<string name="small_screen_position_bottom_right">Bottom Right (Default)</string>
|
||||||
|
<string name="small_screen_position_top_left">Top Left</string>
|
||||||
|
<string name="small_screen_position_middle_left">Middle Left</string>
|
||||||
|
<string name="small_screen_position_bottom_left">Bottom Left</string>
|
||||||
|
<string name="small_screen_position_above">Above</string>
|
||||||
|
<string name="small_screen_position_below">Below</string>
|
||||||
|
<string name="large_screen_proportion">Large Screen Proportion</string>
|
||||||
|
<string name="large_screen_proportion_description">How many times larger is the large screen than the small screen in Large Screen layout?</string>
|
||||||
<string name="emulation_adjust_custom_layout">Adjust Custom Layout in Settings</string>
|
<string name="emulation_adjust_custom_layout">Adjust Custom Layout in Settings</string>
|
||||||
<string name="emulation_landscape_custom_layout">Landscape Custom Layout</string>
|
<string name="emulation_landscape_custom_layout">Landscape Custom Layout</string>
|
||||||
<string name="emulation_portrait_custom_layout">Portrait Custom Layout</string>
|
<string name="emulation_portrait_custom_layout">Portrait Custom Layout</string>
|
||||||
|
|
|
@ -40,7 +40,7 @@ std::string_view GetGraphicsAPIName(GraphicsAPI api) {
|
||||||
|
|
||||||
std::string_view GetTextureFilterName(TextureFilter filter) {
|
std::string_view GetTextureFilterName(TextureFilter filter) {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case TextureFilter::None:
|
case TextureFilter::NoFilter:
|
||||||
return "None";
|
return "None";
|
||||||
case TextureFilter::Anime4K:
|
case TextureFilter::Anime4K:
|
||||||
return "Anime4K";
|
return "Anime4K";
|
||||||
|
@ -112,6 +112,7 @@ void LogSettings() {
|
||||||
log_setting("Layout_SwapScreen", values.swap_screen.GetValue());
|
log_setting("Layout_SwapScreen", values.swap_screen.GetValue());
|
||||||
log_setting("Layout_UprightScreen", values.upright_screen.GetValue());
|
log_setting("Layout_UprightScreen", values.upright_screen.GetValue());
|
||||||
log_setting("Layout_LargeScreenProportion", values.large_screen_proportion.GetValue());
|
log_setting("Layout_LargeScreenProportion", values.large_screen_proportion.GetValue());
|
||||||
|
log_setting("Layout_SmallScreenPosition", values.small_screen_position.GetValue());
|
||||||
log_setting("Utility_DumpTextures", values.dump_textures.GetValue());
|
log_setting("Utility_DumpTextures", values.dump_textures.GetValue());
|
||||||
log_setting("Utility_CustomTextures", values.custom_textures.GetValue());
|
log_setting("Utility_CustomTextures", values.custom_textures.GetValue());
|
||||||
log_setting("Utility_PreloadTextures", values.preload_textures.GetValue());
|
log_setting("Utility_PreloadTextures", values.preload_textures.GetValue());
|
||||||
|
@ -201,6 +202,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||||
values.swap_screen.SetGlobal(true);
|
values.swap_screen.SetGlobal(true);
|
||||||
values.upright_screen.SetGlobal(true);
|
values.upright_screen.SetGlobal(true);
|
||||||
values.large_screen_proportion.SetGlobal(true);
|
values.large_screen_proportion.SetGlobal(true);
|
||||||
|
values.small_screen_position.SetGlobal(true);
|
||||||
values.bg_red.SetGlobal(true);
|
values.bg_red.SetGlobal(true);
|
||||||
values.bg_green.SetGlobal(true);
|
values.bg_green.SetGlobal(true);
|
||||||
values.bg_blue.SetGlobal(true);
|
values.bg_blue.SetGlobal(true);
|
||||||
|
|
|
@ -53,6 +53,20 @@ enum class PortraitLayoutOption : u32 {
|
||||||
PortraitCustomLayout,
|
PortraitCustomLayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Defines where the small screen will appear relative to the large screen
|
||||||
|
* when in Large Screen mode
|
||||||
|
*/
|
||||||
|
enum class SmallScreenPosition : u32 {
|
||||||
|
TopRight,
|
||||||
|
MiddleRight,
|
||||||
|
BottomRight,
|
||||||
|
TopLeft,
|
||||||
|
MiddleLeft,
|
||||||
|
BottomLeft,
|
||||||
|
AboveLarge,
|
||||||
|
BelowLarge
|
||||||
|
};
|
||||||
|
|
||||||
enum class StereoRenderOption : u32 {
|
enum class StereoRenderOption : u32 {
|
||||||
Off = 0,
|
Off = 0,
|
||||||
SideBySide = 1,
|
SideBySide = 1,
|
||||||
|
@ -77,7 +91,7 @@ enum class AudioEmulation : u32 {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TextureFilter : u32 {
|
enum class TextureFilter : u32 {
|
||||||
None = 0,
|
NoFilter = 0,
|
||||||
Anime4K = 1,
|
Anime4K = 1,
|
||||||
Bicubic = 2,
|
Bicubic = 2,
|
||||||
ScaleForce = 3,
|
ScaleForce = 3,
|
||||||
|
@ -479,7 +493,7 @@ struct Values {
|
||||||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||||
SwitchableSetting<double, true> frame_limit{100, 0, 1000, "frame_limit"};
|
SwitchableSetting<double, true> frame_limit{100, 0, 1000, "frame_limit"};
|
||||||
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::None, "texture_filter"};
|
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::NoFilter, "texture_filter"};
|
||||||
SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled,
|
SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled,
|
||||||
"texture_sampling"};
|
"texture_sampling"};
|
||||||
SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"};
|
SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"};
|
||||||
|
@ -487,6 +501,8 @@ struct Values {
|
||||||
SwitchableSetting<bool> upright_screen{false, "upright_screen"};
|
SwitchableSetting<bool> upright_screen{false, "upright_screen"};
|
||||||
SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f,
|
SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f,
|
||||||
"large_screen_proportion"};
|
"large_screen_proportion"};
|
||||||
|
SwitchableSetting<SmallScreenPosition> small_screen_position{SmallScreenPosition::BottomRight,
|
||||||
|
"small_screen_position"};
|
||||||
Setting<u16> custom_top_x{0, "custom_top_x"};
|
Setting<u16> custom_top_x{0, "custom_top_x"};
|
||||||
Setting<u16> custom_top_y{0, "custom_top_y"};
|
Setting<u16> custom_top_y{0, "custom_top_y"};
|
||||||
Setting<u16> custom_top_width{800, "custom_top_width"};
|
Setting<u16> custom_top_width{800, "custom_top_width"};
|
||||||
|
|
|
@ -224,7 +224,7 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||||
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||||
Settings::values.upright_screen.GetValue(),
|
Settings::values.upright_screen.GetValue(),
|
||||||
Settings::values.large_screen_proportion.GetValue(),
|
Settings::values.large_screen_proportion.GetValue(),
|
||||||
Layout::VerticalAlignment::Bottom);
|
Settings::values.small_screen_position.GetValue());
|
||||||
break;
|
break;
|
||||||
case Settings::LayoutOption::HybridScreen:
|
case Settings::LayoutOption::HybridScreen:
|
||||||
layout =
|
layout =
|
||||||
|
@ -235,7 +235,7 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||||
layout =
|
layout =
|
||||||
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||||
Settings::values.upright_screen.GetValue(), 1.0f,
|
Settings::values.upright_screen.GetValue(), 1.0f,
|
||||||
Layout::VerticalAlignment::Bottom);
|
Settings::SmallScreenPosition::MiddleRight);
|
||||||
break;
|
break;
|
||||||
#ifndef ANDROID
|
#ifndef ANDROID
|
||||||
case Settings::LayoutOption::SeparateWindows:
|
case Settings::LayoutOption::SeparateWindows:
|
||||||
|
|
|
@ -15,10 +15,6 @@ static constexpr float TOP_SCREEN_ASPECT_RATIO =
|
||||||
static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth;
|
static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth;
|
||||||
static constexpr float BOT_SCREEN_ASPECT_RATIO =
|
static constexpr float BOT_SCREEN_ASPECT_RATIO =
|
||||||
static_cast<float>(Core::kScreenBottomHeight) / Core::kScreenBottomWidth;
|
static_cast<float>(Core::kScreenBottomHeight) / Core::kScreenBottomWidth;
|
||||||
static constexpr float TOP_SCREEN_UPRIGHT_ASPECT_RATIO =
|
|
||||||
static_cast<float>(Core::kScreenTopWidth) / Core::kScreenTopHeight;
|
|
||||||
static constexpr float BOT_SCREEN_UPRIGHT_ASPECT_RATIO =
|
|
||||||
static_cast<float>(Core::kScreenBottomWidth) / Core::kScreenBottomHeight;
|
|
||||||
|
|
||||||
u32 FramebufferLayout::GetScalingRatio() const {
|
u32 FramebufferLayout::GetScalingRatio() const {
|
||||||
if (is_rotated) {
|
if (is_rotated) {
|
||||||
|
@ -39,115 +35,19 @@ static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
|
FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
|
||||||
ASSERT(width > 0);
|
return LargeFrameLayout(width, height, swapped, upright, 1.0f,
|
||||||
ASSERT(height > 0);
|
Settings::SmallScreenPosition::BelowLarge);
|
||||||
|
|
||||||
FramebufferLayout res{width, height, true, true, {}, {}, !upright};
|
|
||||||
Common::Rectangle<u32> screen_window_area;
|
|
||||||
Common::Rectangle<u32> top_screen;
|
|
||||||
Common::Rectangle<u32> bot_screen;
|
|
||||||
float emulation_aspect_ratio;
|
|
||||||
if (upright) {
|
|
||||||
// Default layout gives equal screen sizes to the top and bottom screen
|
|
||||||
screen_window_area = {0, 0, width / 2, height};
|
|
||||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
|
||||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
|
||||||
// both screens width are taken into account by dividing by 2
|
|
||||||
emulation_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO / 2;
|
|
||||||
} else {
|
|
||||||
// Default layout gives equal screen sizes to the top and bottom screen
|
|
||||||
screen_window_area = {0, 0, width, height / 2};
|
|
||||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
|
||||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
|
||||||
// both screens height are taken into account by multiplying by 2
|
|
||||||
emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
|
||||||
|
|
||||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
|
||||||
// Window is wider than the emulation content => apply borders to the right and left sides
|
|
||||||
if (upright) {
|
|
||||||
// Recalculate the bottom screen to account for the height difference between right and
|
|
||||||
// left
|
|
||||||
screen_window_area = {0, 0, top_screen.GetWidth(), height};
|
|
||||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
|
||||||
bot_screen =
|
|
||||||
bot_screen.TranslateY((top_screen.GetHeight() - bot_screen.GetHeight()) / 2);
|
|
||||||
if (swapped) {
|
|
||||||
bot_screen = bot_screen.TranslateX(width / 2 - bot_screen.GetWidth());
|
|
||||||
} else {
|
|
||||||
top_screen = top_screen.TranslateX(width / 2 - top_screen.GetWidth());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
top_screen =
|
|
||||||
top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
|
|
||||||
bot_screen =
|
|
||||||
bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Window is narrower than the emulation content => apply borders to the top and bottom
|
|
||||||
if (upright) {
|
|
||||||
top_screen = top_screen.TranslateY(
|
|
||||||
(screen_window_area.GetHeight() - top_screen.GetHeight()) / 2);
|
|
||||||
bot_screen = bot_screen.TranslateY(
|
|
||||||
(screen_window_area.GetHeight() - bot_screen.GetHeight()) / 2);
|
|
||||||
} else {
|
|
||||||
// Recalculate the bottom screen to account for the width difference between top and
|
|
||||||
// bottom
|
|
||||||
screen_window_area = {0, 0, width, top_screen.GetHeight()};
|
|
||||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
|
||||||
bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2);
|
|
||||||
if (swapped) {
|
|
||||||
bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight());
|
|
||||||
} else {
|
|
||||||
top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (upright) {
|
|
||||||
// Move the top screen to the right if we are swapped.
|
|
||||||
res.top_screen = swapped ? top_screen.TranslateX(width / 2) : top_screen;
|
|
||||||
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(width / 2);
|
|
||||||
} else {
|
|
||||||
// Move the top screen to the bottom if we are swapped.
|
|
||||||
res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen;
|
|
||||||
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool swapped) {
|
FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool swapped) {
|
||||||
ASSERT(width > 0);
|
ASSERT(width > 0);
|
||||||
ASSERT(height > 0);
|
ASSERT(height > 0);
|
||||||
|
const float scale_factor = swapped ? 1.25f : 0.8f;
|
||||||
FramebufferLayout res{width, height, true, true, {}, {}, true, true};
|
FramebufferLayout res = LargeFrameLayout(width, height, swapped, false, scale_factor,
|
||||||
// Default layout gives equal screen sizes to the top and bottom screen
|
Settings::SmallScreenPosition::BelowLarge);
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height / 2};
|
const int shiftY = -(int)(swapped ? res.bottom_screen.top : res.top_screen.top);
|
||||||
Common::Rectangle<u32> top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
res.top_screen = res.top_screen.TranslateY(shiftY);
|
||||||
Common::Rectangle<u32> bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
res.bottom_screen = res.bottom_screen.TranslateY(shiftY);
|
||||||
|
|
||||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
|
||||||
// both screens height are taken into account by multiplying by 2
|
|
||||||
float emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2;
|
|
||||||
|
|
||||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
|
||||||
// Apply borders to the left and right sides of the window.
|
|
||||||
top_screen =
|
|
||||||
top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
|
|
||||||
bot_screen =
|
|
||||||
bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
|
|
||||||
} else {
|
|
||||||
// Window is narrower than the emulation content
|
|
||||||
// Recalculate the bottom screen to account for the width difference between top and bottom
|
|
||||||
|
|
||||||
bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the top screen to the bottom if we are swapped.
|
|
||||||
res.top_screen = swapped ? top_screen.TranslateY(bot_screen.GetHeight()) : top_screen;
|
|
||||||
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(top_screen.GetHeight());
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,26 +56,22 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
||||||
ASSERT(height > 0);
|
ASSERT(height > 0);
|
||||||
// The drawing code needs at least somewhat valid values for both screens
|
// The drawing code needs at least somewhat valid values for both screens
|
||||||
// so just calculate them both even if the other isn't showing.
|
// so just calculate them both even if the other isn't showing.
|
||||||
|
if (upright) {
|
||||||
|
std::swap(width, height);
|
||||||
|
}
|
||||||
FramebufferLayout res{width, height, !swapped, swapped, {}, {}, !upright};
|
FramebufferLayout res{width, height, !swapped, swapped, {}, {}, !upright};
|
||||||
|
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||||
Common::Rectangle<u32> top_screen;
|
Common::Rectangle<u32> top_screen;
|
||||||
Common::Rectangle<u32> bot_screen;
|
Common::Rectangle<u32> bot_screen;
|
||||||
float emulation_aspect_ratio;
|
float emulation_aspect_ratio;
|
||||||
if (upright) {
|
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
|
||||||
emulation_aspect_ratio =
|
|
||||||
(swapped) ? BOT_SCREEN_UPRIGHT_ASPECT_RATIO : TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
} else {
|
|
||||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
|
||||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
|
||||||
emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool stretched = (Settings::values.screen_top_stretch.GetValue() && !swapped) ||
|
const bool stretched = (Settings::values.screen_top_stretch.GetValue() && !swapped) ||
|
||||||
(Settings::values.screen_bottom_stretch.GetValue() && swapped);
|
(Settings::values.screen_bottom_stretch.GetValue() && swapped);
|
||||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||||
|
|
||||||
if (stretched) {
|
if (stretched) {
|
||||||
top_screen = {Settings::values.screen_top_leftright_padding.GetValue(),
|
top_screen = {Settings::values.screen_top_leftright_padding.GetValue(),
|
||||||
|
@ -197,116 +93,148 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
||||||
}
|
}
|
||||||
res.top_screen = top_screen;
|
res.top_screen = top_screen;
|
||||||
res.bottom_screen = bot_screen;
|
res.bottom_screen = bot_screen;
|
||||||
return res;
|
if (upright) {
|
||||||
|
return reverseLayout(res);
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright,
|
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright,
|
||||||
float scale_factor, VerticalAlignment vertical_alignment) {
|
float scale_factor,
|
||||||
|
Settings::SmallScreenPosition small_screen_position) {
|
||||||
ASSERT(width > 0);
|
ASSERT(width > 0);
|
||||||
ASSERT(height > 0);
|
ASSERT(height > 0);
|
||||||
#ifdef ANDROID
|
|
||||||
vertical_alignment = VerticalAlignment::Top;
|
|
||||||
#endif
|
|
||||||
FramebufferLayout res{width, height, true, true, {}, {}, !upright};
|
|
||||||
// Split the window into two parts. Give 4x width to the main screen and 1x width to the small
|
|
||||||
// To do that, find the total emulation box and maximize that based on window size
|
|
||||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
|
||||||
float emulation_aspect_ratio;
|
|
||||||
float large_screen_aspect_ratio;
|
|
||||||
float small_screen_aspect_ratio;
|
|
||||||
if (upright) {
|
if (upright) {
|
||||||
if (swapped) {
|
std::swap(width, height);
|
||||||
emulation_aspect_ratio =
|
|
||||||
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth) /
|
|
||||||
(Core::kScreenBottomHeight * scale_factor);
|
|
||||||
large_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
small_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
} else {
|
|
||||||
emulation_aspect_ratio =
|
|
||||||
(Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth) /
|
|
||||||
(Core::kScreenTopHeight * scale_factor);
|
|
||||||
large_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
small_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (swapped) {
|
|
||||||
emulation_aspect_ratio =
|
|
||||||
Core::kScreenBottomHeight * scale_factor /
|
|
||||||
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth);
|
|
||||||
large_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
|
|
||||||
small_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
|
|
||||||
} else {
|
|
||||||
emulation_aspect_ratio =
|
|
||||||
Core::kScreenTopHeight * scale_factor /
|
|
||||||
(Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth);
|
|
||||||
large_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
|
|
||||||
small_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const bool vertical = (small_screen_position == Settings::SmallScreenPosition::AboveLarge ||
|
||||||
|
small_screen_position == Settings::SmallScreenPosition::BelowLarge);
|
||||||
|
FramebufferLayout res{width, height, true, true, {}, {}, !upright};
|
||||||
|
// Split the window into two parts. Give proportional width to the smaller screen
|
||||||
|
// To do that, find the total emulation box and maximize that based on window size
|
||||||
|
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||||
|
float emulation_aspect_ratio;
|
||||||
|
|
||||||
|
float large_height =
|
||||||
|
swapped ? Core::kScreenBottomHeight * scale_factor : Core::kScreenTopHeight * scale_factor;
|
||||||
|
float small_height =
|
||||||
|
static_cast<float>(swapped ? Core::kScreenTopHeight : Core::kScreenBottomHeight);
|
||||||
|
float large_width =
|
||||||
|
swapped ? Core::kScreenBottomWidth * scale_factor : Core::kScreenTopWidth * scale_factor;
|
||||||
|
float small_width =
|
||||||
|
static_cast<float>(swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth);
|
||||||
|
|
||||||
|
float emulation_width, emulation_height;
|
||||||
|
if (vertical) {
|
||||||
|
// width is just the larger size at this point
|
||||||
|
emulation_width = std::max(large_width, small_width);
|
||||||
|
emulation_height = large_height + small_height;
|
||||||
|
} else {
|
||||||
|
emulation_width = large_width + small_width;
|
||||||
|
emulation_height = std::max(large_height, small_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
emulation_aspect_ratio = emulation_height / emulation_width;
|
||||||
|
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||||
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||||
Common::Rectangle<u32> large_screen = MaxRectangle(total_rect, large_screen_aspect_ratio);
|
const float scale_amount = total_rect.GetHeight() * 1.f / emulation_height * 1.f;
|
||||||
Common::Rectangle<u32> scaled_rect = total_rect.Scale(1.f / scale_factor);
|
Common::Rectangle<u32> large_screen =
|
||||||
Common::Rectangle<u32> small_screen = MaxRectangle(scaled_rect, small_screen_aspect_ratio);
|
Common::Rectangle<u32>{total_rect.left, total_rect.top,
|
||||||
|
static_cast<u32>(large_width * scale_amount + total_rect.left),
|
||||||
|
static_cast<u32>(large_height * scale_amount + total_rect.top)};
|
||||||
|
Common::Rectangle<u32> small_screen =
|
||||||
|
Common::Rectangle<u32>{total_rect.left, total_rect.top,
|
||||||
|
static_cast<u32>(small_width * scale_amount + total_rect.left),
|
||||||
|
static_cast<u32>(small_height * scale_amount + total_rect.top)};
|
||||||
|
|
||||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||||
|
// shift the large screen so it is at the left position of the bounding rectangle
|
||||||
large_screen = large_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
large_screen = large_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
||||||
} else {
|
} else {
|
||||||
|
// shift the large screen so it is at the top position of the bounding rectangle
|
||||||
large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
|
large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
|
||||||
}
|
}
|
||||||
if (upright) {
|
|
||||||
large_screen = large_screen.TranslateY(small_screen.GetHeight());
|
|
||||||
small_screen = small_screen.TranslateY(large_screen.top - small_screen.GetHeight());
|
|
||||||
switch (vertical_alignment) {
|
|
||||||
case VerticalAlignment::Top:
|
|
||||||
// Shift the small screen to the top right corner
|
|
||||||
small_screen = small_screen.TranslateX(large_screen.left);
|
|
||||||
break;
|
|
||||||
case VerticalAlignment::Middle:
|
|
||||||
// Shift the small screen to the center right
|
|
||||||
small_screen = small_screen.TranslateX(
|
|
||||||
((large_screen.GetWidth() - small_screen.GetWidth()) / 2) + large_screen.left);
|
|
||||||
break;
|
|
||||||
case VerticalAlignment::Bottom:
|
|
||||||
// Shift the small screen to the bottom right corner
|
|
||||||
small_screen = small_screen.TranslateX(large_screen.right - small_screen.GetWidth());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
switch (small_screen_position) {
|
||||||
|
case Settings::SmallScreenPosition::TopRight:
|
||||||
|
// Shift the small screen to the top right corner
|
||||||
small_screen = small_screen.TranslateX(large_screen.right);
|
small_screen = small_screen.TranslateX(large_screen.right);
|
||||||
switch (vertical_alignment) {
|
small_screen = small_screen.TranslateY(large_screen.top);
|
||||||
case VerticalAlignment::Top:
|
break;
|
||||||
// Shift the small screen to the top right corner
|
case Settings::SmallScreenPosition::MiddleRight:
|
||||||
small_screen = small_screen.TranslateY(large_screen.top);
|
// Shift the small screen to the center right
|
||||||
break;
|
small_screen = small_screen.TranslateX(large_screen.right);
|
||||||
case VerticalAlignment::Middle:
|
small_screen = small_screen.TranslateY(
|
||||||
// Shift the small screen to the center right
|
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||||
small_screen = small_screen.TranslateY(
|
break;
|
||||||
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
case Settings::SmallScreenPosition::BottomRight:
|
||||||
break;
|
// Shift the small screen to the bottom right corner
|
||||||
case VerticalAlignment::Bottom:
|
small_screen = small_screen.TranslateX(large_screen.right);
|
||||||
// Shift the small screen to the bottom right corner
|
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
break;
|
||||||
break;
|
case Settings::SmallScreenPosition::TopLeft:
|
||||||
default:
|
// shift the small screen to the upper left then shift the large screen to its right
|
||||||
UNREACHABLE();
|
small_screen = small_screen.TranslateX(large_screen.left);
|
||||||
break;
|
large_screen = large_screen.TranslateX(small_screen.GetWidth());
|
||||||
|
small_screen = small_screen.TranslateY(large_screen.top);
|
||||||
|
break;
|
||||||
|
case Settings::SmallScreenPosition::MiddleLeft:
|
||||||
|
// shift the small screen to the middle left and shift the large screen to its right
|
||||||
|
small_screen = small_screen.TranslateX(large_screen.left);
|
||||||
|
large_screen = large_screen.TranslateX(small_screen.GetWidth());
|
||||||
|
small_screen = small_screen.TranslateY(
|
||||||
|
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||||
|
break;
|
||||||
|
case Settings::SmallScreenPosition::BottomLeft:
|
||||||
|
// shift the small screen to the bottom left and shift the large screen to its right
|
||||||
|
small_screen = small_screen.TranslateX(large_screen.left);
|
||||||
|
large_screen = large_screen.TranslateX(small_screen.GetWidth());
|
||||||
|
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||||
|
break;
|
||||||
|
case Settings::SmallScreenPosition::AboveLarge:
|
||||||
|
// shift the large screen down and the bottom screen above it
|
||||||
|
small_screen = small_screen.TranslateY(large_screen.top);
|
||||||
|
large_screen = large_screen.TranslateY(small_screen.GetHeight());
|
||||||
|
// If the "large screen" is actually smaller, center it
|
||||||
|
if (large_screen.GetWidth() < total_rect.GetWidth()) {
|
||||||
|
large_screen =
|
||||||
|
large_screen.TranslateX((total_rect.GetWidth() - large_screen.GetWidth()) / 2);
|
||||||
}
|
}
|
||||||
|
small_screen = small_screen.TranslateX(large_screen.left + large_screen.GetWidth() / 2 -
|
||||||
|
small_screen.GetWidth() / 2);
|
||||||
|
break;
|
||||||
|
case Settings::SmallScreenPosition::BelowLarge:
|
||||||
|
// shift the bottom_screen down and then over to the center
|
||||||
|
// If the "large screen" is actually smaller, center it
|
||||||
|
if (large_screen.GetWidth() < total_rect.GetWidth()) {
|
||||||
|
large_screen =
|
||||||
|
large_screen.TranslateX((total_rect.GetWidth() - large_screen.GetWidth()) / 2);
|
||||||
|
}
|
||||||
|
small_screen = small_screen.TranslateY(large_screen.bottom);
|
||||||
|
small_screen = small_screen.TranslateX(large_screen.left + large_screen.GetWidth() / 2 -
|
||||||
|
small_screen.GetWidth() / 2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
res.top_screen = swapped ? small_screen : large_screen;
|
res.top_screen = swapped ? small_screen : large_screen;
|
||||||
res.bottom_screen = swapped ? large_screen : small_screen;
|
res.bottom_screen = swapped ? large_screen : small_screen;
|
||||||
return res;
|
if (upright) {
|
||||||
|
return reverseLayout(res);
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool upright) {
|
FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool upright) {
|
||||||
ASSERT(width > 0);
|
ASSERT(width > 0);
|
||||||
ASSERT(height > 0);
|
ASSERT(height > 0);
|
||||||
|
if (upright) {
|
||||||
|
std::swap(width, height);
|
||||||
|
}
|
||||||
FramebufferLayout res{width, height, true, true, {}, {}, !upright, false, true, {}};
|
FramebufferLayout res{width, height, true, true, {}, {}, !upright, false, true, {}};
|
||||||
|
|
||||||
// Split the window into two parts. Give 2.25x width to the main screen,
|
// Split the window into two parts. Give 2.25x width to the main screen,
|
||||||
|
@ -327,13 +255,6 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
|
||||||
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth);
|
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upright) {
|
|
||||||
hybrid_area_aspect_ratio = 1.f / hybrid_area_aspect_ratio;
|
|
||||||
main_screen_aspect_ratio = 1.f / main_screen_aspect_ratio;
|
|
||||||
top_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
bot_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||||
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, hybrid_area_aspect_ratio);
|
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, hybrid_area_aspect_ratio);
|
||||||
Common::Rectangle<u32> large_main_screen = MaxRectangle(total_rect, main_screen_aspect_ratio);
|
Common::Rectangle<u32> large_main_screen = MaxRectangle(total_rect, main_screen_aspect_ratio);
|
||||||
|
@ -349,32 +270,24 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
|
||||||
|
|
||||||
// Scale the bottom screen so it's width is the same as top screen
|
// Scale the bottom screen so it's width is the same as top screen
|
||||||
small_bottom_screen = small_bottom_screen.Scale(1.25f);
|
small_bottom_screen = small_bottom_screen.Scale(1.25f);
|
||||||
if (upright) {
|
|
||||||
large_main_screen = large_main_screen.TranslateY(small_bottom_screen.GetHeight());
|
|
||||||
// Shift small bottom screen to upper right corner
|
|
||||||
small_bottom_screen =
|
|
||||||
small_bottom_screen.TranslateX(large_main_screen.right - small_bottom_screen.GetWidth())
|
|
||||||
.TranslateY(large_main_screen.top - small_bottom_screen.GetHeight());
|
|
||||||
|
|
||||||
// Shift small top screen to upper left corner
|
// Shift the small bottom screen to the bottom right corner
|
||||||
small_top_screen = small_top_screen.TranslateX(large_main_screen.left)
|
small_bottom_screen = small_bottom_screen.TranslateX(large_main_screen.right)
|
||||||
.TranslateY(large_main_screen.top - small_bottom_screen.GetHeight());
|
.TranslateY(large_main_screen.GetHeight() + large_main_screen.top -
|
||||||
} else {
|
small_bottom_screen.GetHeight());
|
||||||
// Shift the small bottom screen to the bottom right corner
|
|
||||||
small_bottom_screen =
|
|
||||||
small_bottom_screen.TranslateX(large_main_screen.right)
|
|
||||||
.TranslateY(large_main_screen.GetHeight() + large_main_screen.top -
|
|
||||||
small_bottom_screen.GetHeight());
|
|
||||||
|
|
||||||
// Shift small top screen to upper right corner
|
// Shift small top screen to upper right corner
|
||||||
small_top_screen =
|
small_top_screen =
|
||||||
small_top_screen.TranslateX(large_main_screen.right).TranslateY(large_main_screen.top);
|
small_top_screen.TranslateX(large_main_screen.right).TranslateY(large_main_screen.top);
|
||||||
}
|
|
||||||
|
|
||||||
res.top_screen = small_top_screen;
|
res.top_screen = small_top_screen;
|
||||||
res.additional_screen = swapped ? small_bottom_screen : large_main_screen;
|
res.additional_screen = swapped ? small_bottom_screen : large_main_screen;
|
||||||
res.bottom_screen = swapped ? large_main_screen : small_bottom_screen;
|
res.bottom_screen = swapped ? large_main_screen : small_bottom_screen;
|
||||||
return res;
|
if (upright) {
|
||||||
|
return reverseLayout(res);
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright) {
|
FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright) {
|
||||||
|
@ -387,25 +300,30 @@ FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary
|
||||||
FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool is_portrait_mode) {
|
FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool is_portrait_mode) {
|
||||||
ASSERT(width > 0);
|
ASSERT(width > 0);
|
||||||
ASSERT(height > 0);
|
ASSERT(height > 0);
|
||||||
|
const bool upright = Settings::values.upright_screen.GetValue();
|
||||||
|
if (upright) {
|
||||||
|
std::swap(width, height);
|
||||||
|
}
|
||||||
FramebufferLayout res{
|
FramebufferLayout res{
|
||||||
width, height, true, true, {}, {}, !Settings::values.upright_screen, is_portrait_mode};
|
width, height, true, true, {}, {}, !Settings::values.upright_screen, is_portrait_mode};
|
||||||
u16 top_x = is_portrait_mode ? Settings::values.custom_portrait_top_x.GetValue()
|
const u16 top_x = is_portrait_mode ? Settings::values.custom_portrait_top_x.GetValue()
|
||||||
: Settings::values.custom_top_x.GetValue();
|
: Settings::values.custom_top_x.GetValue();
|
||||||
u16 top_width = is_portrait_mode ? Settings::values.custom_portrait_top_width.GetValue()
|
const u16 top_width = is_portrait_mode ? Settings::values.custom_portrait_top_width.GetValue()
|
||||||
: Settings::values.custom_top_width.GetValue();
|
: Settings::values.custom_top_width.GetValue();
|
||||||
u16 top_y = is_portrait_mode ? Settings::values.custom_portrait_top_y.GetValue()
|
const u16 top_y = is_portrait_mode ? Settings::values.custom_portrait_top_y.GetValue()
|
||||||
: Settings::values.custom_top_y.GetValue();
|
: Settings::values.custom_top_y.GetValue();
|
||||||
u16 top_height = is_portrait_mode ? Settings::values.custom_portrait_top_height.GetValue()
|
const u16 top_height = is_portrait_mode ? Settings::values.custom_portrait_top_height.GetValue()
|
||||||
: Settings::values.custom_top_height.GetValue();
|
: Settings::values.custom_top_height.GetValue();
|
||||||
u16 bottom_x = is_portrait_mode ? Settings::values.custom_portrait_bottom_x.GetValue()
|
const u16 bottom_x = is_portrait_mode ? Settings::values.custom_portrait_bottom_x.GetValue()
|
||||||
: Settings::values.custom_bottom_x.GetValue();
|
: Settings::values.custom_bottom_x.GetValue();
|
||||||
u16 bottom_width = is_portrait_mode ? Settings::values.custom_portrait_bottom_width.GetValue()
|
const u16 bottom_width = is_portrait_mode
|
||||||
: Settings::values.custom_bottom_width.GetValue();
|
? Settings::values.custom_portrait_bottom_width.GetValue()
|
||||||
u16 bottom_y = is_portrait_mode ? Settings::values.custom_portrait_bottom_y.GetValue()
|
: Settings::values.custom_bottom_width.GetValue();
|
||||||
: Settings::values.custom_bottom_y.GetValue();
|
const u16 bottom_y = is_portrait_mode ? Settings::values.custom_portrait_bottom_y.GetValue()
|
||||||
u16 bottom_height = is_portrait_mode ? Settings::values.custom_portrait_bottom_height.GetValue()
|
: Settings::values.custom_bottom_y.GetValue();
|
||||||
: Settings::values.custom_bottom_height.GetValue();
|
const u16 bottom_height = is_portrait_mode
|
||||||
|
? Settings::values.custom_portrait_bottom_height.GetValue()
|
||||||
|
: Settings::values.custom_bottom_height.GetValue();
|
||||||
|
|
||||||
Common::Rectangle<u32> top_screen{top_x, top_y, (u32)(top_x + top_width),
|
Common::Rectangle<u32> top_screen{top_x, top_y, (u32)(top_x + top_width),
|
||||||
(u32)(top_y + top_height)};
|
(u32)(top_y + top_height)};
|
||||||
|
@ -419,7 +337,11 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool
|
||||||
res.top_screen = top_screen;
|
res.top_screen = top_screen;
|
||||||
res.bottom_screen = bot_screen;
|
res.bottom_screen = bot_screen;
|
||||||
}
|
}
|
||||||
return res;
|
if (upright) {
|
||||||
|
return reverseLayout(res);
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary,
|
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary,
|
||||||
|
@ -479,30 +401,37 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar
|
||||||
Settings::values.upright_screen.GetValue());
|
Settings::values.upright_screen.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
case Settings::LayoutOption::LargeScreen:
|
case Settings::LayoutOption::LargeScreen: {
|
||||||
if (Settings::values.swap_screen.GetValue()) {
|
const bool swapped = Settings::values.swap_screen.GetValue();
|
||||||
width =
|
const int largeWidth = swapped ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
||||||
(Core::kScreenBottomWidth +
|
const int largeHeight = swapped ? Core::kScreenBottomHeight : Core::kScreenTopHeight;
|
||||||
Core::kScreenTopWidth /
|
const int smallWidth =
|
||||||
static_cast<int>(Settings::values.large_screen_proportion.GetValue())) *
|
static_cast<int>((swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth) /
|
||||||
res_scale;
|
Settings::values.large_screen_proportion.GetValue());
|
||||||
height = Core::kScreenBottomHeight * res_scale;
|
const int smallHeight =
|
||||||
|
static_cast<int>((swapped ? Core::kScreenTopHeight : Core::kScreenBottomHeight) /
|
||||||
|
Settings::values.large_screen_proportion.GetValue());
|
||||||
|
|
||||||
|
if (Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::AboveLarge ||
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::BelowLarge) {
|
||||||
|
// vertical, so height is sum of heights, width is larger of widths
|
||||||
|
width = std::max(largeWidth, smallWidth) * res_scale;
|
||||||
|
height = (largeHeight + smallHeight) * res_scale;
|
||||||
} else {
|
} else {
|
||||||
width =
|
width = (largeWidth + smallWidth) * res_scale;
|
||||||
(Core::kScreenTopWidth +
|
height = std::max(largeHeight, smallHeight) * res_scale;
|
||||||
Core::kScreenBottomWidth /
|
|
||||||
static_cast<int>(Settings::values.large_screen_proportion.GetValue())) *
|
|
||||||
res_scale;
|
|
||||||
height = Core::kScreenTopHeight * res_scale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.upright_screen.GetValue()) {
|
if (Settings::values.upright_screen.GetValue()) {
|
||||||
std::swap(width, height);
|
std::swap(width, height);
|
||||||
}
|
}
|
||||||
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||||
Settings::values.upright_screen.GetValue(),
|
Settings::values.upright_screen.GetValue(),
|
||||||
Settings::values.large_screen_proportion.GetValue(),
|
Settings::values.large_screen_proportion.GetValue(),
|
||||||
VerticalAlignment::Bottom);
|
Settings::values.small_screen_position.GetValue());
|
||||||
|
}
|
||||||
case Settings::LayoutOption::SideScreen:
|
case Settings::LayoutOption::SideScreen:
|
||||||
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
||||||
height = Core::kScreenTopHeight * res_scale;
|
height = Core::kScreenTopHeight * res_scale;
|
||||||
|
@ -512,7 +441,7 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar
|
||||||
}
|
}
|
||||||
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||||
Settings::values.upright_screen.GetValue(), 1,
|
Settings::values.upright_screen.GetValue(), 1,
|
||||||
VerticalAlignment::Middle);
|
Settings::SmallScreenPosition::MiddleRight);
|
||||||
|
|
||||||
case Settings::LayoutOption::Default:
|
case Settings::LayoutOption::Default:
|
||||||
default:
|
default:
|
||||||
|
@ -613,10 +542,44 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) {
|
||||||
return new_layout;
|
return new_layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FramebufferLayout reverseLayout(FramebufferLayout layout) {
|
||||||
|
std::swap(layout.height, layout.width);
|
||||||
|
u32 oldLeft, oldRight, oldTop, oldBottom;
|
||||||
|
|
||||||
|
oldLeft = layout.top_screen.left;
|
||||||
|
oldRight = layout.top_screen.right;
|
||||||
|
oldTop = layout.top_screen.top;
|
||||||
|
oldBottom = layout.top_screen.bottom;
|
||||||
|
layout.top_screen.left = oldTop;
|
||||||
|
layout.top_screen.right = oldBottom;
|
||||||
|
layout.top_screen.top = layout.height - oldRight;
|
||||||
|
layout.top_screen.bottom = layout.height - oldLeft;
|
||||||
|
|
||||||
|
oldLeft = layout.bottom_screen.left;
|
||||||
|
oldRight = layout.bottom_screen.right;
|
||||||
|
oldTop = layout.bottom_screen.top;
|
||||||
|
oldBottom = layout.bottom_screen.bottom;
|
||||||
|
layout.bottom_screen.left = oldTop;
|
||||||
|
layout.bottom_screen.right = oldBottom;
|
||||||
|
layout.bottom_screen.top = layout.height - oldRight;
|
||||||
|
layout.bottom_screen.bottom = layout.height - oldLeft;
|
||||||
|
|
||||||
|
if (layout.additional_screen_enabled) {
|
||||||
|
oldLeft = layout.additional_screen.left;
|
||||||
|
oldRight = layout.additional_screen.right;
|
||||||
|
oldTop = layout.additional_screen.top;
|
||||||
|
oldBottom = layout.additional_screen.bottom;
|
||||||
|
layout.additional_screen.left = oldTop;
|
||||||
|
layout.additional_screen.right = oldBottom;
|
||||||
|
layout.additional_screen.top = layout.height - oldRight;
|
||||||
|
layout.additional_screen.bottom = layout.height - oldLeft;
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<unsigned, unsigned> GetMinimumSizeFromPortraitLayout() {
|
std::pair<unsigned, unsigned> GetMinimumSizeFromPortraitLayout() {
|
||||||
u32 min_width, min_height;
|
const u32 min_width = Core::kScreenTopWidth;
|
||||||
min_width = Core::kScreenTopWidth;
|
const u32 min_height = Core::kScreenTopHeight + Core::kScreenBottomHeight;
|
||||||
min_height = Core::kScreenTopHeight + Core::kScreenBottomHeight;
|
|
||||||
return std::make_pair(min_width, min_height);
|
return std::make_pair(min_width, min_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,15 +595,30 @@ std::pair<unsigned, unsigned> GetMinimumSizeFromLayout(Settings::LayoutOption la
|
||||||
min_width = Settings::values.swap_screen ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
min_width = Settings::values.swap_screen ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
||||||
min_height = Core::kScreenBottomHeight;
|
min_height = Core::kScreenBottomHeight;
|
||||||
break;
|
break;
|
||||||
case Settings::LayoutOption::LargeScreen:
|
case Settings::LayoutOption::LargeScreen: {
|
||||||
min_width = static_cast<u32>(
|
const bool swapped = Settings::values.swap_screen.GetValue();
|
||||||
Settings::values.swap_screen
|
const int largeWidth = swapped ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
||||||
? Core::kScreenTopWidth / Settings::values.large_screen_proportion.GetValue() +
|
const int largeHeight = swapped ? Core::kScreenBottomHeight : Core::kScreenTopHeight;
|
||||||
Core::kScreenBottomWidth
|
int smallWidth = swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth;
|
||||||
: Core::kScreenTopWidth + Core::kScreenBottomWidth /
|
int smallHeight = swapped ? Core::kScreenTopHeight : Core::kScreenBottomHeight;
|
||||||
Settings::values.large_screen_proportion.GetValue());
|
smallWidth =
|
||||||
min_height = Core::kScreenBottomHeight;
|
static_cast<int>(smallWidth / Settings::values.large_screen_proportion.GetValue());
|
||||||
|
smallHeight =
|
||||||
|
static_cast<int>(smallHeight / Settings::values.large_screen_proportion.GetValue());
|
||||||
|
min_width = static_cast<u32>(Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::AboveLarge ||
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::BelowLarge
|
||||||
|
? std::max(largeWidth, smallWidth)
|
||||||
|
: largeWidth + smallWidth);
|
||||||
|
min_height = static_cast<u32>(Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::AboveLarge ||
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::BelowLarge
|
||||||
|
? largeHeight + smallHeight
|
||||||
|
: std::max(largeHeight, smallHeight));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case Settings::LayoutOption::SideScreen:
|
case Settings::LayoutOption::SideScreen:
|
||||||
min_width = Core::kScreenTopWidth + Core::kScreenBottomWidth;
|
min_width = Core::kScreenTopWidth + Core::kScreenBottomWidth;
|
||||||
min_height = Core::kScreenBottomHeight;
|
min_height = Core::kScreenBottomHeight;
|
||||||
|
|
|
@ -5,10 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
|
#include "common/settings.h"
|
||||||
namespace Settings {
|
|
||||||
enum class LayoutOption : u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
|
@ -20,31 +17,6 @@ enum class DisplayOrientation {
|
||||||
PortraitFlipped, // 3DS rotated 270 degrees counter-clockwise
|
PortraitFlipped, // 3DS rotated 270 degrees counter-clockwise
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes the vertical alignment of the top and bottom screens in LargeFrameLayout
|
|
||||||
/// Top
|
|
||||||
/// +-------------+-----+
|
|
||||||
/// | | |
|
|
||||||
/// | +-----+
|
|
||||||
/// | |
|
|
||||||
/// +-------------+
|
|
||||||
/// Middle
|
|
||||||
/// +-------------+
|
|
||||||
/// | +-----+
|
|
||||||
/// | | |
|
|
||||||
/// | +-----+
|
|
||||||
/// +-------------+
|
|
||||||
/// Bottom
|
|
||||||
/// +-------------+
|
|
||||||
/// | |
|
|
||||||
/// | +-----+
|
|
||||||
/// | | |
|
|
||||||
/// +-------------+-----+
|
|
||||||
enum class VerticalAlignment {
|
|
||||||
Top,
|
|
||||||
Middle,
|
|
||||||
Bottom,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Describes the horizontal coordinates for the right eye screen when using Cardboard VR
|
/// Describes the horizontal coordinates for the right eye screen when using Cardboard VR
|
||||||
struct CardboardSettings {
|
struct CardboardSettings {
|
||||||
u32 top_screen_right_eye;
|
u32 top_screen_right_eye;
|
||||||
|
@ -75,7 +47,12 @@ struct FramebufferLayout {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method for constructing a default FramebufferLayout
|
* Method to create a rotated copy of a framebuffer layout, used to rotate to upright mode
|
||||||
|
*/
|
||||||
|
FramebufferLayout reverseLayout(FramebufferLayout layout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method for constructing a default FramebufferLayout with screens on top of one another
|
||||||
* @param width Window framebuffer width in pixels
|
* @param width Window framebuffer width in pixels
|
||||||
* @param height Window framebuffer height in pixels
|
* @param height Window framebuffer height in pixels
|
||||||
* @param is_swapped if true, the bottom screen will be displayed above the top screen
|
* @param is_swapped if true, the bottom screen will be displayed above the top screen
|
||||||
|
@ -105,9 +82,7 @@ FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool is_swap
|
||||||
FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
|
FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method for constructing a Frame with the a 4x size Top screen with a 1x size bottom
|
* Factory method for constructing a Frame with differently sized top and bottom windows
|
||||||
* screen on the right
|
|
||||||
* This is useful in particular because it matches well with a 1920x1080 resolution monitor
|
|
||||||
* @param width Window framebuffer width in pixels
|
* @param width Window framebuffer width in pixels
|
||||||
* @param height Window framebuffer height in pixels
|
* @param height Window framebuffer height in pixels
|
||||||
* @param is_swapped if true, the bottom screen will be the large display
|
* @param is_swapped if true, the bottom screen will be the large display
|
||||||
|
@ -118,7 +93,8 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool
|
||||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||||
*/
|
*/
|
||||||
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool upright,
|
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool upright,
|
||||||
float scale_factor, VerticalAlignment vertical_alignment);
|
float scale_factor,
|
||||||
|
Settings::SmallScreenPosition small_screen_position);
|
||||||
/**
|
/**
|
||||||
* Factory method for constructing a frame with 2.5 times bigger top screen on the right,
|
* Factory method for constructing a frame with 2.5 times bigger top screen on the right,
|
||||||
* and 1x top and bottom screen on the left
|
* and 1x top and bottom screen on the left
|
||||||
|
@ -126,6 +102,9 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool
|
||||||
* @param height Window framebuffer height in pixels
|
* @param height Window framebuffer height in pixels
|
||||||
* @param is_swapped if true, the bottom screen will be the large display
|
* @param is_swapped if true, the bottom screen will be the large display
|
||||||
* @param upright if true, the screens will be rotated 90 degrees anti-clockwise
|
* @param upright if true, the screens will be rotated 90 degrees anti-clockwise
|
||||||
|
* @param scale_factor determines the proportion of large to small. Must be >= 1
|
||||||
|
* @param small_screen_position determines where the small screen appears relative to the large
|
||||||
|
* screen
|
||||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||||
*/
|
*/
|
||||||
FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool upright);
|
FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool upright);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -1099,8 +1099,8 @@ void Module::Interface::DriverInitialize(Kernel::HLERequestContext& ctx) {
|
||||||
for (int context_id = 0; context_id < 2; ++context_id) {
|
for (int context_id = 0; context_id < 2; ++context_id) {
|
||||||
// Note: the following default values are verified against real 3DS
|
// Note: the following default values are verified against real 3DS
|
||||||
ContextConfig& context = camera.contexts[context_id];
|
ContextConfig& context = camera.contexts[context_id];
|
||||||
context.flip = camera_id == 1 ? Flip::Horizontal : Flip::None;
|
context.flip = camera_id == 1 ? Flip::Horizontal : Flip::NoFlip;
|
||||||
context.effect = Effect::None;
|
context.effect = Effect::NoEffect;
|
||||||
context.format = OutputFormat::YUV422;
|
context.format = OutputFormat::YUV422;
|
||||||
context.resolution =
|
context.resolution =
|
||||||
context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/];
|
context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -656,8 +656,8 @@ private:
|
||||||
Result SetPackageParameter(const PackageParameterType& package);
|
Result SetPackageParameter(const PackageParameterType& package);
|
||||||
|
|
||||||
struct ContextConfig {
|
struct ContextConfig {
|
||||||
Flip flip{Flip::None};
|
Flip flip{Flip::NoFlip};
|
||||||
Effect effect{Effect::None};
|
Effect effect{Effect::NoEffect};
|
||||||
OutputFormat format{OutputFormat::YUV422};
|
OutputFormat format{OutputFormat::YUV422};
|
||||||
Resolution resolution = {0, 0, 0, 0, 0, 0};
|
Resolution resolution = {0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ enum CameraIndex {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Effect : u8 {
|
enum class Effect : u8 {
|
||||||
None = 0,
|
NoEffect = 0,
|
||||||
Mono = 1,
|
Mono = 1,
|
||||||
Sepia = 2,
|
Sepia = 2,
|
||||||
Negative = 3,
|
Negative = 3,
|
||||||
|
@ -26,7 +26,7 @@ enum class Effect : u8 {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Flip : u8 {
|
enum class Flip : u8 {
|
||||||
None = 0,
|
NoFlip = 0,
|
||||||
Horizontal = 1,
|
Horizontal = 1,
|
||||||
Vertical = 2,
|
Vertical = 2,
|
||||||
Reverse = 3,
|
Reverse = 3,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ void QtCameraInterface::SetFlip(Service::CAM::Flip flip) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtCameraInterface::SetEffect(Service::CAM::Effect effect) {
|
void QtCameraInterface::SetEffect(Service::CAM::Effect effect) {
|
||||||
if (effect != Service::CAM::Effect::None) {
|
if (effect != Service::CAM::Effect::NoEffect) {
|
||||||
LOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect));
|
LOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,6 +518,7 @@ void QtConfig::ReadLayoutValues() {
|
||||||
ReadGlobalSetting(Settings::values.swap_screen);
|
ReadGlobalSetting(Settings::values.swap_screen);
|
||||||
ReadGlobalSetting(Settings::values.upright_screen);
|
ReadGlobalSetting(Settings::values.upright_screen);
|
||||||
ReadGlobalSetting(Settings::values.large_screen_proportion);
|
ReadGlobalSetting(Settings::values.large_screen_proportion);
|
||||||
|
ReadGlobalSetting(Settings::values.small_screen_position);
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
ReadBasicSetting(Settings::values.mono_render_option);
|
ReadBasicSetting(Settings::values.mono_render_option);
|
||||||
|
@ -1077,7 +1078,7 @@ void QtConfig::SaveLayoutValues() {
|
||||||
WriteGlobalSetting(Settings::values.swap_screen);
|
WriteGlobalSetting(Settings::values.swap_screen);
|
||||||
WriteGlobalSetting(Settings::values.upright_screen);
|
WriteGlobalSetting(Settings::values.upright_screen);
|
||||||
WriteGlobalSetting(Settings::values.large_screen_proportion);
|
WriteGlobalSetting(Settings::values.large_screen_proportion);
|
||||||
|
WriteGlobalSetting(Settings::values.small_screen_position);
|
||||||
if (global) {
|
if (global) {
|
||||||
WriteBasicSetting(Settings::values.mono_render_option);
|
WriteBasicSetting(Settings::values.mono_render_option);
|
||||||
WriteBasicSetting(Settings::values.custom_top_x);
|
WriteBasicSetting(Settings::values.custom_top_x);
|
||||||
|
|
|
@ -206,8 +206,8 @@ void ConfigureCamera::StartPreviewing() {
|
||||||
}
|
}
|
||||||
previewing_camera->SetResolution(
|
previewing_camera->SetResolution(
|
||||||
{static_cast<u16>(preview_width), static_cast<u16>(preview_height)});
|
{static_cast<u16>(preview_width), static_cast<u16>(preview_height)});
|
||||||
previewing_camera->SetEffect(Service::CAM::Effect::None);
|
previewing_camera->SetEffect(Service::CAM::Effect::NoEffect);
|
||||||
previewing_camera->SetFlip(Service::CAM::Flip::None);
|
previewing_camera->SetFlip(Service::CAM::Flip::NoFlip);
|
||||||
previewing_camera->SetFormat(Service::CAM::OutputFormat::RGB565);
|
previewing_camera->SetFormat(Service::CAM::OutputFormat::RGB565);
|
||||||
previewing_camera->SetFrameRate(Service::CAM::FrameRate::Rate_30);
|
previewing_camera->SetFrameRate(Service::CAM::FrameRate::Rate_30);
|
||||||
previewing_camera->StartCapture();
|
previewing_camera->StartCapture();
|
||||||
|
|
|
@ -28,6 +28,15 @@ ConfigureLayout::ConfigureLayout(QWidget* parent)
|
||||||
currentIndex == (uint)(Settings::LayoutOption::LargeScreen));
|
currentIndex == (uint)(Settings::LayoutOption::LargeScreen));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui->small_screen_position_combobox->setEnabled(
|
||||||
|
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::LargeScreen));
|
||||||
|
connect(ui->layout_combobox,
|
||||||
|
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||||
|
[this](int currentIndex) {
|
||||||
|
ui->small_screen_position_combobox->setEnabled(
|
||||||
|
currentIndex == (uint)(Settings::LayoutOption::LargeScreen));
|
||||||
|
});
|
||||||
|
|
||||||
ui->single_screen_layout_config_group->setEnabled(
|
ui->single_screen_layout_config_group->setEnabled(
|
||||||
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SingleScreen) ||
|
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SingleScreen) ||
|
||||||
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows));
|
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows));
|
||||||
|
@ -116,7 +125,8 @@ void ConfigureLayout::SetConfiguration() {
|
||||||
ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue());
|
ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue());
|
||||||
ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue());
|
ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue());
|
||||||
ui->large_screen_proportion->setValue(Settings::values.large_screen_proportion.GetValue());
|
ui->large_screen_proportion->setValue(Settings::values.large_screen_proportion.GetValue());
|
||||||
|
ui->small_screen_position_combobox->setCurrentIndex(
|
||||||
|
static_cast<int>(Settings::values.small_screen_position.GetValue()));
|
||||||
ui->custom_top_x->setValue(Settings::values.custom_top_x.GetValue());
|
ui->custom_top_x->setValue(Settings::values.custom_top_x.GetValue());
|
||||||
ui->custom_top_y->setValue(Settings::values.custom_top_y.GetValue());
|
ui->custom_top_y->setValue(Settings::values.custom_top_y.GetValue());
|
||||||
ui->custom_top_width->setValue(Settings::values.custom_top_width.GetValue());
|
ui->custom_top_width->setValue(Settings::values.custom_top_width.GetValue());
|
||||||
|
@ -153,7 +163,8 @@ void ConfigureLayout::RetranslateUI() {
|
||||||
|
|
||||||
void ConfigureLayout::ApplyConfiguration() {
|
void ConfigureLayout::ApplyConfiguration() {
|
||||||
Settings::values.large_screen_proportion = ui->large_screen_proportion->value();
|
Settings::values.large_screen_proportion = ui->large_screen_proportion->value();
|
||||||
|
Settings::values.small_screen_position = static_cast<Settings::SmallScreenPosition>(
|
||||||
|
ui->small_screen_position_combobox->currentIndex());
|
||||||
Settings::values.custom_top_x = ui->custom_top_x->value();
|
Settings::values.custom_top_x = ui->custom_top_x->value();
|
||||||
Settings::values.custom_top_y = ui->custom_top_y->value();
|
Settings::values.custom_top_y = ui->custom_top_y->value();
|
||||||
Settings::values.custom_top_width = ui->custom_top_width->value();
|
Settings::values.custom_top_width = ui->custom_top_width->value();
|
||||||
|
|
|
@ -145,6 +145,75 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="small_pos_widget" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="small_pos_layout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="small_pos_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Small Screen Position</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="small_screen_position_combobox">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Upper Right</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Middle Right</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Bottom Right (default)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Upper Left</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Middle Left</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Bottom Left</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Above large screen</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Below large screen</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="bg_color_group" native="true">
|
<widget class="QWidget" name="bg_color_group" native="true">
|
||||||
<layout class="QHBoxLayout" name="bg_color_group_2">
|
<layout class="QHBoxLayout" name="bg_color_group_2">
|
||||||
|
@ -592,6 +661,7 @@
|
||||||
<tabstop>toggle_swap_screen</tabstop>
|
<tabstop>toggle_swap_screen</tabstop>
|
||||||
<tabstop>toggle_upright_screen</tabstop>
|
<tabstop>toggle_upright_screen</tabstop>
|
||||||
<tabstop>large_screen_proportion</tabstop>
|
<tabstop>large_screen_proportion</tabstop>
|
||||||
|
<tabstop>small_screen_position_combobox</tabstop>
|
||||||
<tabstop>bg_button</tabstop>
|
<tabstop>bg_button</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
|
@ -580,6 +580,16 @@ void GMainWindow::InitializeWidgets() {
|
||||||
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Separate_Windows);
|
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Separate_Windows);
|
||||||
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Hybrid_Screen);
|
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Hybrid_Screen);
|
||||||
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Custom_Layout);
|
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Custom_Layout);
|
||||||
|
|
||||||
|
QActionGroup* actionGroup_SmallPositions = new QActionGroup(this);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_TopRight);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_MiddleRight);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_BottomRight);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_TopLeft);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_MiddleLeft);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_BottomLeft);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_Above);
|
||||||
|
actionGroup_SmallPositions->addAction(ui->action_Small_Screen_Below);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::InitializeDebugWidgets() {
|
void GMainWindow::InitializeDebugWidgets() {
|
||||||
|
@ -1032,6 +1042,14 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect_menu(ui->action_Screen_Layout_Custom_Layout, &GMainWindow::ChangeScreenLayout);
|
connect_menu(ui->action_Screen_Layout_Custom_Layout, &GMainWindow::ChangeScreenLayout);
|
||||||
connect_menu(ui->action_Screen_Layout_Swap_Screens, &GMainWindow::OnSwapScreens);
|
connect_menu(ui->action_Screen_Layout_Swap_Screens, &GMainWindow::OnSwapScreens);
|
||||||
connect_menu(ui->action_Screen_Layout_Upright_Screens, &GMainWindow::OnRotateScreens);
|
connect_menu(ui->action_Screen_Layout_Upright_Screens, &GMainWindow::OnRotateScreens);
|
||||||
|
connect_menu(ui->action_Small_Screen_TopRight, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_MiddleRight, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_BottomRight, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_TopLeft, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_MiddleLeft, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_BottomLeft, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_Above, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
connect_menu(ui->action_Small_Screen_Below, &GMainWindow::ChangeSmallScreenPosition);
|
||||||
|
|
||||||
// Movie
|
// Movie
|
||||||
connect_menu(ui->action_Record_Movie, &GMainWindow::OnRecordMovie);
|
connect_menu(ui->action_Record_Movie, &GMainWindow::OnRecordMovie);
|
||||||
|
@ -2548,13 +2566,13 @@ void GMainWindow::UpdateSecondaryWindowVisibility() {
|
||||||
|
|
||||||
void GMainWindow::ChangeScreenLayout() {
|
void GMainWindow::ChangeScreenLayout() {
|
||||||
Settings::LayoutOption new_layout = Settings::LayoutOption::Default;
|
Settings::LayoutOption new_layout = Settings::LayoutOption::Default;
|
||||||
|
|
||||||
if (ui->action_Screen_Layout_Default->isChecked()) {
|
if (ui->action_Screen_Layout_Default->isChecked()) {
|
||||||
new_layout = Settings::LayoutOption::Default;
|
new_layout = Settings::LayoutOption::Default;
|
||||||
} else if (ui->action_Screen_Layout_Single_Screen->isChecked()) {
|
} else if (ui->action_Screen_Layout_Single_Screen->isChecked()) {
|
||||||
new_layout = Settings::LayoutOption::SingleScreen;
|
new_layout = Settings::LayoutOption::SingleScreen;
|
||||||
} else if (ui->action_Screen_Layout_Large_Screen->isChecked()) {
|
} else if (ui->action_Screen_Layout_Large_Screen->isChecked()) {
|
||||||
new_layout = Settings::LayoutOption::LargeScreen;
|
new_layout = Settings::LayoutOption::LargeScreen;
|
||||||
|
ui->menu_Small_Screen_Position->setEnabled(true);
|
||||||
} else if (ui->action_Screen_Layout_Hybrid_Screen->isChecked()) {
|
} else if (ui->action_Screen_Layout_Hybrid_Screen->isChecked()) {
|
||||||
new_layout = Settings::LayoutOption::HybridScreen;
|
new_layout = Settings::LayoutOption::HybridScreen;
|
||||||
} else if (ui->action_Screen_Layout_Side_by_Side->isChecked()) {
|
} else if (ui->action_Screen_Layout_Side_by_Side->isChecked()) {
|
||||||
|
@ -2566,6 +2584,34 @@ void GMainWindow::ChangeScreenLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::values.layout_option = new_layout;
|
Settings::values.layout_option = new_layout;
|
||||||
|
SyncMenuUISettings();
|
||||||
|
system.ApplySettings();
|
||||||
|
UpdateSecondaryWindowVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::ChangeSmallScreenPosition() {
|
||||||
|
Settings::SmallScreenPosition new_position = Settings::SmallScreenPosition::BottomRight;
|
||||||
|
|
||||||
|
if (ui->action_Small_Screen_TopRight->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::TopRight;
|
||||||
|
} else if (ui->action_Small_Screen_MiddleRight->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::MiddleRight;
|
||||||
|
} else if (ui->action_Small_Screen_BottomRight->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::BottomRight;
|
||||||
|
} else if (ui->action_Small_Screen_TopLeft->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::TopLeft;
|
||||||
|
} else if (ui->action_Small_Screen_MiddleLeft->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::MiddleLeft;
|
||||||
|
} else if (ui->action_Small_Screen_BottomLeft->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::BottomLeft;
|
||||||
|
} else if (ui->action_Small_Screen_Above->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::AboveLarge;
|
||||||
|
} else if (ui->action_Small_Screen_Below->isChecked()) {
|
||||||
|
new_position = Settings::SmallScreenPosition::BelowLarge;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::values.small_screen_position = new_position;
|
||||||
|
SyncMenuUISettings();
|
||||||
system.ApplySettings();
|
system.ApplySettings();
|
||||||
UpdateSecondaryWindowVisibility();
|
UpdateSecondaryWindowVisibility();
|
||||||
}
|
}
|
||||||
|
@ -3637,6 +3683,31 @@ void GMainWindow::SyncMenuUISettings() {
|
||||||
ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen.GetValue());
|
ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen.GetValue());
|
||||||
ui->action_Screen_Layout_Upright_Screens->setChecked(
|
ui->action_Screen_Layout_Upright_Screens->setChecked(
|
||||||
Settings::values.upright_screen.GetValue());
|
Settings::values.upright_screen.GetValue());
|
||||||
|
|
||||||
|
ui->menu_Small_Screen_Position->setEnabled(Settings::values.layout_option.GetValue() ==
|
||||||
|
Settings::LayoutOption::LargeScreen);
|
||||||
|
|
||||||
|
ui->action_Small_Screen_TopRight->setChecked(
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::TopRight);
|
||||||
|
ui->action_Small_Screen_MiddleRight->setChecked(
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::MiddleRight);
|
||||||
|
ui->action_Small_Screen_BottomRight->setChecked(
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::BottomRight);
|
||||||
|
ui->action_Small_Screen_TopLeft->setChecked(Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::TopLeft);
|
||||||
|
ui->action_Small_Screen_MiddleLeft->setChecked(
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::MiddleLeft);
|
||||||
|
ui->action_Small_Screen_BottomLeft->setChecked(
|
||||||
|
Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::BottomLeft);
|
||||||
|
ui->action_Small_Screen_Above->setChecked(Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::AboveLarge);
|
||||||
|
ui->action_Small_Screen_Below->setChecked(Settings::values.small_screen_position.GetValue() ==
|
||||||
|
Settings::SmallScreenPosition::BelowLarge);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::RetranslateStatusBar() {
|
void GMainWindow::RetranslateStatusBar() {
|
||||||
|
|
|
@ -265,6 +265,7 @@ private slots:
|
||||||
void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
void ToggleSecondaryFullscreen();
|
void ToggleSecondaryFullscreen();
|
||||||
void ChangeScreenLayout();
|
void ChangeScreenLayout();
|
||||||
|
void ChangeSmallScreenPosition();
|
||||||
void UpdateSecondaryWindowVisibility();
|
void UpdateSecondaryWindowVisibility();
|
||||||
void ToggleScreenLayout();
|
void ToggleScreenLayout();
|
||||||
void OnSwapScreens();
|
void OnSwapScreens();
|
||||||
|
|
|
@ -142,6 +142,23 @@
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="action_Screen_Layout_Upright_Screens"/>
|
<addaction name="action_Screen_Layout_Upright_Screens"/>
|
||||||
<addaction name="action_Screen_Layout_Swap_Screens"/>
|
<addaction name="action_Screen_Layout_Swap_Screens"/>
|
||||||
|
<widget class="QMenu" name="menu_Small_Screen_Position">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Small Screen Position</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="action_Small_Screen_TopRight"/>
|
||||||
|
<addaction name="action_Small_Screen_MiddleRight"/>
|
||||||
|
<addaction name="action_Small_Screen_BottomRight"/>
|
||||||
|
<addaction name="action_Small_Screen_TopLeft"/>
|
||||||
|
<addaction name="action_Small_Screen_MiddleLeft"/>
|
||||||
|
<addaction name="action_Small_Screen_BottomLeft"/>
|
||||||
|
<addaction name="action_Small_Screen_Above"/>
|
||||||
|
<addaction name="action_Small_Screen_Below"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menu_Small_Screen_Position"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="action_Fullscreen"/>
|
<addaction name="action_Fullscreen"/>
|
||||||
<addaction name="action_Single_Window_Mode"/>
|
<addaction name="action_Single_Window_Mode"/>
|
||||||
|
@ -541,6 +558,70 @@
|
||||||
<string>Custom Layout</string>
|
<string>Custom Layout</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Small_Screen_TopRight">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Top Right</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_MiddleRight">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Middle Right</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_BottomRight">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Bottom Right</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_TopLeft">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Top Left</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_MiddleLeft">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Middle Left</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_BottomLeft">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Bottom Left</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_Above">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Above</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Small_Screen_Below">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Below</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_Screen_Layout_Swap_Screens">
|
<action name="action_Screen_Layout_Swap_Screens">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2022 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -553,7 +553,7 @@ SurfaceId RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo
|
||||||
params.levels = max_level + 1;
|
params.levels = max_level + 1;
|
||||||
params.is_tiled = true;
|
params.is_tiled = true;
|
||||||
params.pixel_format = PixelFormatFromTextureFormat(info.format);
|
params.pixel_format = PixelFormatFromTextureFormat(info.format);
|
||||||
params.res_scale = filter != Settings::TextureFilter::None ? resolution_scale_factor : 1;
|
params.res_scale = filter != Settings::TextureFilter::NoFilter ? resolution_scale_factor : 1;
|
||||||
params.UpdateParams();
|
params.UpdateParams();
|
||||||
|
|
||||||
const u32 min_width = info.width >> max_level;
|
const u32 min_width = info.width >> max_level;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||||
const auto filter = Settings::values.texture_filter.GetValue();
|
const auto filter = Settings::values.texture_filter.GetValue();
|
||||||
const bool is_depth =
|
const bool is_depth =
|
||||||
surface.type == SurfaceType::Depth || surface.type == SurfaceType::DepthStencil;
|
surface.type == SurfaceType::Depth || surface.type == SurfaceType::DepthStencil;
|
||||||
if (filter == Settings::TextureFilter::None || is_depth) {
|
if (filter == Settings::TextureFilter::NoFilter || is_depth) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (blit.src_level != 0) {
|
if (blit.src_level != 0) {
|
||||||
|
|
Loading…
Reference in a new issue