mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2024-10-31 23:45:07 +01: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
@ -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) {
|
||||
// These must match what is defined in src/common/settings.h
|
||||
TOP_FULL_WIDTH(0),
|
||||
@ -28,7 +45,7 @@ enum class PortraitScreenLayout(val int: Int) {
|
||||
|
||||
companion object {
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -9,7 +9,7 @@ enum class FloatSetting(
|
||||
override val section: String,
|
||||
override val defaultValue: Float
|
||||
) : AbstractFloatSetting {
|
||||
// There are no float settings currently
|
||||
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
|
||||
EMPTY_SETTING("", "", 0.0f);
|
||||
|
||||
override var float: Float = defaultValue
|
||||
|
@ -23,6 +23,7 @@ enum class IntSetting(
|
||||
CARDBOARD_X_SHIFT("cardboard_x_shift", Settings.SECTION_LAYOUT, 0),
|
||||
CARDBOARD_Y_SHIFT("cardboard_y_shift", 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_Y("custom_top_y",Settings.SECTION_LAYOUT,0),
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -39,5 +39,6 @@ abstract class SettingsItem(
|
||||
const val TYPE_RUNNABLE = 7
|
||||
const val TYPE_INPUT_BINDING = 8
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -23,17 +23,16 @@ class SliderSetting(
|
||||
val defaultValue: Float? = null
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_SLIDER
|
||||
|
||||
val selectedValue: Int
|
||||
val selectedFloat: Float
|
||||
get() {
|
||||
val setting = setting ?: return defaultValue!!.toInt()
|
||||
val setting = setting ?: return defaultValue!!.toFloat()
|
||||
return when (setting) {
|
||||
is AbstractIntSetting -> setting.int
|
||||
is FloatSetting -> setting.float.roundToInt()
|
||||
is ScaledFloatSetting -> setting.float.roundToInt()
|
||||
is AbstractIntSetting -> setting.int.toFloat()
|
||||
is FloatSetting -> setting.float
|
||||
is ScaledFloatSetting -> setting.float
|
||||
else -> {
|
||||
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.text.Editable
|
||||
import android.text.InputFilter
|
||||
import android.text.InputType
|
||||
import android.text.TextWatcher
|
||||
import android.text.format.DateFormat
|
||||
import android.view.LayoutInflater
|
||||
@ -68,6 +69,7 @@ import io.github.lime3ds.android.utils.SystemSaveGame
|
||||
import java.lang.IllegalStateException
|
||||
import java.lang.NumberFormatException
|
||||
import java.text.SimpleDateFormat
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class SettingsAdapter(
|
||||
private val fragmentView: SettingsFragmentView,
|
||||
@ -77,7 +79,7 @@ class SettingsAdapter(
|
||||
private var clickedItem: SettingsItem? = null
|
||||
private var clickedPosition: Int
|
||||
private var dialog: AlertDialog? = null
|
||||
private var sliderProgress = 0
|
||||
private var sliderProgress = 0f
|
||||
private var textSliderValue: TextInputEditText? = null
|
||||
private var textInputLayout: TextInputLayout? = null
|
||||
private var textInputValue: String = ""
|
||||
@ -136,27 +138,23 @@ class SettingsAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
getItem(position)?.let { holder.bind(it) }
|
||||
}
|
||||
|
||||
private fun getItem(position: Int): SettingsItem {
|
||||
return settings!![position]
|
||||
private fun getItem(position: Int): SettingsItem? {
|
||||
return settings?.get(position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (settings != null) {
|
||||
settings!!.size
|
||||
} else {
|
||||
0
|
||||
}
|
||||
return settings?.size ?: 0
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return getItem(position).type
|
||||
return getItem(position)?.type ?: -1
|
||||
}
|
||||
|
||||
fun setSettingsList(settings: ArrayList<SettingsItem>?) {
|
||||
this.settings = settings
|
||||
this.settings = settings ?: arrayListOf()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@ -182,10 +180,12 @@ class SettingsAdapter(
|
||||
|
||||
private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) {
|
||||
clickedItem = item
|
||||
dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(item.nameId)
|
||||
.setSingleChoiceItems(item.choices, item.selectValueIndex, this)
|
||||
.show()
|
||||
dialog = context?.let {
|
||||
MaterialAlertDialogBuilder(it)
|
||||
.setTitle(item.nameId)
|
||||
.setSingleChoiceItems(item.choices, item.selectValueIndex, this)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
|
||||
@ -231,10 +231,10 @@ class SettingsAdapter(
|
||||
.build()
|
||||
|
||||
datePicker.addOnPositiveButtonClickListener {
|
||||
timePicker.show(
|
||||
(fragmentView.activityView as AppCompatActivity).supportFragmentManager,
|
||||
"TimePicker"
|
||||
)
|
||||
val activity = fragmentView.activityView as? AppCompatActivity
|
||||
activity?.supportFragmentManager?.let { fragmentManager ->
|
||||
timePicker.show(fragmentManager, "TimePicker")
|
||||
}
|
||||
}
|
||||
timePicker.addOnPositiveButtonClickListener {
|
||||
var epochTime: Long = datePicker.selection!! / 1000
|
||||
@ -258,38 +258,62 @@ class SettingsAdapter(
|
||||
fun onSliderClick(item: SliderSetting, position: Int) {
|
||||
clickedItem = item
|
||||
clickedPosition = position
|
||||
sliderProgress = item.selectedValue
|
||||
sliderProgress = (item.selectedFloat * 100f).roundToInt() / 100f
|
||||
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val sliderBinding = DialogSliderBinding.inflate(inflater)
|
||||
textInputLayout = sliderBinding.textInput
|
||||
textSliderValue = sliderBinding.textValue
|
||||
textSliderValue!!.setText(sliderProgress.toString())
|
||||
textInputLayout!!.suffixText = item.units
|
||||
if (item.setting is FloatSetting) {
|
||||
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 {
|
||||
valueFrom = item.min.toFloat()
|
||||
valueTo = item.max.toFloat()
|
||||
value = sliderProgress.toFloat()
|
||||
textSliderValue!!.addTextChangedListener( object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
val textValue = s.toString().toIntOrNull();
|
||||
if (textValue == null || textValue < valueFrom || textValue > valueTo) {
|
||||
textInputLayout!!.error ="Inappropriate value"
|
||||
} else {
|
||||
textInputLayout!!.error = null
|
||||
value = textValue.toFloat();
|
||||
}
|
||||
value = sliderProgress
|
||||
textSliderValue?.addTextChangedListener(object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
var textValue = s.toString().toFloatOrNull();
|
||||
if (item.setting !is FloatSetting) {
|
||||
textValue = textValue?.roundToInt()?.toFloat();
|
||||
}
|
||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
})
|
||||
if (textValue == null || textValue < valueFrom || textValue > valueTo) {
|
||||
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 ->
|
||||
sliderProgress = value.toInt()
|
||||
if (textSliderValue!!.text.toString() != value.toInt().toString()) {
|
||||
textSliderValue!!.setText(value.toInt().toString())
|
||||
textSliderValue!!.setSelection(textSliderValue!!.length())
|
||||
sliderProgress = (value * 100).roundToInt().toFloat() / 100f
|
||||
var sliderString = sliderProgress.toString()
|
||||
if (item.setting !is FloatSetting) {
|
||||
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)
|
||||
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
|
||||
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
|
||||
sliderBinding.slider.value = when (item.setting) {
|
||||
sliderBinding.slider?.value = when (item.setting) {
|
||||
is ScaledFloatSetting -> {
|
||||
val scaledSetting = item.setting as ScaledFloatSetting
|
||||
scaledSetting.defaultValue * scaledSetting.scale
|
||||
}
|
||||
|
||||
is FloatSetting -> (item.setting as FloatSetting).defaultValue
|
||||
else -> item.defaultValue!!
|
||||
else -> item.defaultValue ?: 0f
|
||||
}
|
||||
onClick(dialog, which)
|
||||
}
|
||||
@ -358,85 +382,89 @@ class SettingsAdapter(
|
||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||
when (clickedItem) {
|
||||
is SingleChoiceSetting -> {
|
||||
val scSetting = clickedItem as SingleChoiceSetting
|
||||
val setting = when (scSetting.setting) {
|
||||
is AbstractIntSetting -> {
|
||||
val value = getValueForSingleChoiceSelection(scSetting, which)
|
||||
if (scSetting.selectedValue != value) {
|
||||
fragmentView.onSettingChanged()
|
||||
val scSetting = clickedItem as? SingleChoiceSetting
|
||||
scSetting?.let {
|
||||
val setting = when (it.setting) {
|
||||
is AbstractIntSetting -> {
|
||||
val value = getValueForSingleChoiceSelection(it, which)
|
||||
if (it.selectedValue != value) {
|
||||
fragmentView?.onSettingChanged()
|
||||
}
|
||||
it.setSelectedValue(value)
|
||||
}
|
||||
scSetting.setSelectedValue(value)
|
||||
}
|
||||
|
||||
is AbstractShortSetting -> {
|
||||
val value = getValueForSingleChoiceSelection(scSetting, which).toShort()
|
||||
if (scSetting.selectedValue.toShort() != value) {
|
||||
fragmentView.onSettingChanged()
|
||||
is AbstractShortSetting -> {
|
||||
val value = getValueForSingleChoiceSelection(it, which).toShort()
|
||||
if (it.selectedValue.toShort() != value) {
|
||||
fragmentView?.onSettingChanged()
|
||||
}
|
||||
it.setSelectedValue(value)
|
||||
}
|
||||
scSetting.setSelectedValue(value)
|
||||
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
|
||||
fragmentView?.putSetting(setting)
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
fragmentView.putSetting(setting)
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
is StringSingleChoiceSetting -> {
|
||||
val scSetting = clickedItem as StringSingleChoiceSetting
|
||||
val setting = when (scSetting.setting) {
|
||||
is AbstractStringSetting -> {
|
||||
val value = scSetting.getValueAt(which)
|
||||
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
||||
scSetting.setSelectedValue(value!!)
|
||||
val scSetting = clickedItem as? StringSingleChoiceSetting
|
||||
scSetting?.let {
|
||||
val setting = when (it.setting) {
|
||||
is AbstractStringSetting -> {
|
||||
val value = it.getValueAt(which)
|
||||
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 -> {
|
||||
if (scSetting.selectValueIndex != which) fragmentView.onSettingChanged()
|
||||
scSetting.setSelectedValue(scSetting.getValueAt(which)?.toShort() ?: 1)
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("Unrecognized type used for StringSingleChoiceSetting!")
|
||||
fragmentView?.putSetting(setting)
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
fragmentView.putSetting(setting)
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
is SliderSetting -> {
|
||||
val sliderSetting = clickedItem as SliderSetting
|
||||
if (sliderSetting.selectedValue != sliderProgress) {
|
||||
fragmentView.onSettingChanged()
|
||||
}
|
||||
when (sliderSetting.setting) {
|
||||
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)
|
||||
val sliderSetting = clickedItem as? SliderSetting
|
||||
sliderSetting?.let {
|
||||
val sliderval = (it.selectedFloat * 100).roundToInt().toFloat() / 100
|
||||
if (sliderval != sliderProgress) {
|
||||
fragmentView?.onSettingChanged()
|
||||
}
|
||||
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 -> {
|
||||
val inputSetting = clickedItem as StringInputSetting
|
||||
if (inputSetting.selectedValue != textInputValue) {
|
||||
fragmentView.onSettingChanged()
|
||||
}
|
||||
val setting = inputSetting.setSelectedValue(textInputValue)
|
||||
fragmentView.putSetting(setting)
|
||||
closeDialog()
|
||||
val inputSetting = clickedItem as? StringInputSetting
|
||||
inputSetting?.let {
|
||||
if (it.selectedValue != textInputValue) {
|
||||
fragmentView?.onSettingChanged()
|
||||
}
|
||||
val setting = it.setSelectedValue(textInputValue ?: "")
|
||||
fragmentView?.putSetting(setting)
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
clickedItem = null
|
||||
sliderProgress = -1
|
||||
sliderProgress = -1f
|
||||
textInputValue = ""
|
||||
}
|
||||
|
||||
@ -473,7 +501,7 @@ class SettingsAdapter(
|
||||
R.string.setting_not_editable_description
|
||||
).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
|
||||
|
||||
fun onClickRegenerateConsoleId() {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.regenerate_console_id)
|
||||
|
@ -15,6 +15,8 @@ import android.text.TextUtils
|
||||
import androidx.preference.PreferenceManager
|
||||
import io.github.lime3ds.android.LimeApplication
|
||||
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.AbstractIntSetting
|
||||
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.StringSetting
|
||||
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.HeaderSetting
|
||||
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
|
||||
)
|
||||
)
|
||||
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(
|
||||
SubmenuSetting(
|
||||
R.string.emulation_landscape_custom_layout,
|
||||
@ -954,8 +980,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
Settings.SECTION_CUSTOM_PORTRAIT
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,10 +182,11 @@ void Config::ReadValues() {
|
||||
layoutInt = static_cast<int>(Settings::LayoutOption::LargeScreen);
|
||||
}
|
||||
Settings::values.layout_option = static_cast<Settings::LayoutOption>(layoutInt);
|
||||
|
||||
Settings::values.large_screen_proportion =
|
||||
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_y);
|
||||
ReadSetting("Layout", Settings::values.custom_top_width);
|
||||
|
@ -180,15 +180,26 @@ filter_mode =
|
||||
|
||||
[Layout]
|
||||
# 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,
|
||||
# 2: *currently unsupported on android*
|
||||
# 2: Large Screen (Default on android)
|
||||
# 3: Side by Side
|
||||
# 4: Hybrid
|
||||
# 5: Custom Layout
|
||||
# 6: (default) Large screen / small screen
|
||||
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
|
||||
# 0x, 0y is the top left corner of the render window.
|
||||
# suggested aspect ratio for top screen is 5:3
|
||||
|
@ -39,6 +39,28 @@
|
||||
<item>1</item>
|
||||
</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">
|
||||
<item>@string/auto_select</item>
|
||||
<item>@string/system_region_jpn</item>
|
||||
|
@ -382,6 +382,18 @@
|
||||
<string name="emulation_screen_layout_original">Original</string>
|
||||
<string name="emulation_portrait_layout_top_full">Default</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_landscape_custom_layout">Landscape 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) {
|
||||
switch (filter) {
|
||||
case TextureFilter::None:
|
||||
case TextureFilter::NoFilter:
|
||||
return "None";
|
||||
case TextureFilter::Anime4K:
|
||||
return "Anime4K";
|
||||
@ -112,6 +112,7 @@ void LogSettings() {
|
||||
log_setting("Layout_SwapScreen", values.swap_screen.GetValue());
|
||||
log_setting("Layout_UprightScreen", values.upright_screen.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_CustomTextures", values.custom_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.upright_screen.SetGlobal(true);
|
||||
values.large_screen_proportion.SetGlobal(true);
|
||||
values.small_screen_position.SetGlobal(true);
|
||||
values.bg_red.SetGlobal(true);
|
||||
values.bg_green.SetGlobal(true);
|
||||
values.bg_blue.SetGlobal(true);
|
||||
|
@ -53,6 +53,20 @@ enum class PortraitLayoutOption : u32 {
|
||||
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 {
|
||||
Off = 0,
|
||||
SideBySide = 1,
|
||||
@ -77,7 +91,7 @@ enum class AudioEmulation : u32 {
|
||||
};
|
||||
|
||||
enum class TextureFilter : u32 {
|
||||
None = 0,
|
||||
NoFilter = 0,
|
||||
Anime4K = 1,
|
||||
Bicubic = 2,
|
||||
ScaleForce = 3,
|
||||
@ -479,7 +493,7 @@ struct Values {
|
||||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||
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,
|
||||
"texture_sampling"};
|
||||
SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"};
|
||||
@ -487,6 +501,8 @@ struct Values {
|
||||
SwitchableSetting<bool> upright_screen{false, "upright_screen"};
|
||||
SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f,
|
||||
"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_y{0, "custom_top_y"};
|
||||
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(),
|
||||
Settings::values.upright_screen.GetValue(),
|
||||
Settings::values.large_screen_proportion.GetValue(),
|
||||
Layout::VerticalAlignment::Bottom);
|
||||
Settings::values.small_screen_position.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::HybridScreen:
|
||||
layout =
|
||||
@ -235,7 +235,7 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||
layout =
|
||||
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(), 1.0f,
|
||||
Layout::VerticalAlignment::Bottom);
|
||||
Settings::SmallScreenPosition::MiddleRight);
|
||||
break;
|
||||
#ifndef ANDROID
|
||||
case Settings::LayoutOption::SeparateWindows:
|
||||
|
@ -15,10 +15,6 @@ static constexpr float TOP_SCREEN_ASPECT_RATIO =
|
||||
static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth;
|
||||
static constexpr float BOT_SCREEN_ASPECT_RATIO =
|
||||
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 {
|
||||
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) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
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;
|
||||
return LargeFrameLayout(width, height, swapped, upright, 1.0f,
|
||||
Settings::SmallScreenPosition::BelowLarge);
|
||||
}
|
||||
|
||||
FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool swapped) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
FramebufferLayout res{width, height, true, true, {}, {}, true, true};
|
||||
// Default layout gives equal screen sizes to the top and bottom screen
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height / 2};
|
||||
Common::Rectangle<u32> top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
Common::Rectangle<u32> bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
|
||||
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());
|
||||
|
||||
const float scale_factor = swapped ? 1.25f : 0.8f;
|
||||
FramebufferLayout res = LargeFrameLayout(width, height, swapped, false, scale_factor,
|
||||
Settings::SmallScreenPosition::BelowLarge);
|
||||
const int shiftY = -(int)(swapped ? res.bottom_screen.top : res.top_screen.top);
|
||||
res.top_screen = res.top_screen.TranslateY(shiftY);
|
||||
res.bottom_screen = res.bottom_screen.TranslateY(shiftY);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -156,26 +56,22 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
||||
ASSERT(height > 0);
|
||||
// The drawing code needs at least somewhat valid values for both screens
|
||||
// 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};
|
||||
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> top_screen;
|
||||
Common::Rectangle<u32> bot_screen;
|
||||
float emulation_aspect_ratio;
|
||||
if (upright) {
|
||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_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;
|
||||
}
|
||||
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) ||
|
||||
(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) {
|
||||
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.bottom_screen = bot_screen;
|
||||
return res;
|
||||
if (upright) {
|
||||
return reverseLayout(res);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
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(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 (swapped) {
|
||||
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;
|
||||
}
|
||||
std::swap(width, height);
|
||||
}
|
||||
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> total_rect = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
Common::Rectangle<u32> large_screen = MaxRectangle(total_rect, large_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> scaled_rect = total_rect.Scale(1.f / scale_factor);
|
||||
Common::Rectangle<u32> small_screen = MaxRectangle(scaled_rect, small_screen_aspect_ratio);
|
||||
const float scale_amount = total_rect.GetHeight() * 1.f / emulation_height * 1.f;
|
||||
Common::Rectangle<u32> large_screen =
|
||||
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) {
|
||||
// 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);
|
||||
} 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);
|
||||
}
|
||||
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);
|
||||
switch (vertical_alignment) {
|
||||
case VerticalAlignment::Top:
|
||||
// Shift the small screen to the top right corner
|
||||
small_screen = small_screen.TranslateY(large_screen.top);
|
||||
break;
|
||||
case VerticalAlignment::Middle:
|
||||
// Shift the small screen to the center right
|
||||
small_screen = small_screen.TranslateY(
|
||||
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||
break;
|
||||
case VerticalAlignment::Bottom:
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
small_screen = small_screen.TranslateY(large_screen.top);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::MiddleRight:
|
||||
// Shift the small screen to the center right
|
||||
small_screen = small_screen.TranslateX(large_screen.right);
|
||||
small_screen = small_screen.TranslateY(
|
||||
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::BottomRight:
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen = small_screen.TranslateX(large_screen.right);
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||
break;
|
||||
case Settings::SmallScreenPosition::TopLeft:
|
||||
// shift the small screen to the upper left then 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.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.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) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
if (upright) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
FramebufferLayout res{width, height, true, true, {}, {}, !upright, false, true, {}};
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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> total_rect = MaxRectangle(screen_window_area, hybrid_area_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
|
||||
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
|
||||
small_top_screen = small_top_screen.TranslateX(large_main_screen.left)
|
||||
.TranslateY(large_main_screen.top - small_bottom_screen.GetHeight());
|
||||
} else {
|
||||
// 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 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
|
||||
small_top_screen =
|
||||
small_top_screen.TranslateX(large_main_screen.right).TranslateY(large_main_screen.top);
|
||||
}
|
||||
// Shift small top screen to upper right corner
|
||||
small_top_screen =
|
||||
small_top_screen.TranslateX(large_main_screen.right).TranslateY(large_main_screen.top);
|
||||
|
||||
res.top_screen = small_top_screen;
|
||||
res.additional_screen = swapped ? small_bottom_screen : large_main_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) {
|
||||
@ -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) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
const bool upright = Settings::values.upright_screen.GetValue();
|
||||
if (upright) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
FramebufferLayout res{
|
||||
width, height, true, true, {}, {}, !Settings::values.upright_screen, is_portrait_mode};
|
||||
u16 top_x = is_portrait_mode ? Settings::values.custom_portrait_top_x.GetValue()
|
||||
: Settings::values.custom_top_x.GetValue();
|
||||
u16 top_width = is_portrait_mode ? Settings::values.custom_portrait_top_width.GetValue()
|
||||
: Settings::values.custom_top_width.GetValue();
|
||||
u16 top_y = is_portrait_mode ? Settings::values.custom_portrait_top_y.GetValue()
|
||||
: Settings::values.custom_top_y.GetValue();
|
||||
u16 top_height = is_portrait_mode ? Settings::values.custom_portrait_top_height.GetValue()
|
||||
: Settings::values.custom_top_height.GetValue();
|
||||
u16 bottom_x = is_portrait_mode ? Settings::values.custom_portrait_bottom_x.GetValue()
|
||||
: Settings::values.custom_bottom_x.GetValue();
|
||||
u16 bottom_width = is_portrait_mode ? Settings::values.custom_portrait_bottom_width.GetValue()
|
||||
: Settings::values.custom_bottom_width.GetValue();
|
||||
u16 bottom_y = is_portrait_mode ? Settings::values.custom_portrait_bottom_y.GetValue()
|
||||
: Settings::values.custom_bottom_y.GetValue();
|
||||
u16 bottom_height = is_portrait_mode ? Settings::values.custom_portrait_bottom_height.GetValue()
|
||||
: Settings::values.custom_bottom_height.GetValue();
|
||||
const u16 top_x = is_portrait_mode ? Settings::values.custom_portrait_top_x.GetValue()
|
||||
: Settings::values.custom_top_x.GetValue();
|
||||
const u16 top_width = is_portrait_mode ? Settings::values.custom_portrait_top_width.GetValue()
|
||||
: Settings::values.custom_top_width.GetValue();
|
||||
const u16 top_y = is_portrait_mode ? Settings::values.custom_portrait_top_y.GetValue()
|
||||
: Settings::values.custom_top_y.GetValue();
|
||||
const u16 top_height = is_portrait_mode ? Settings::values.custom_portrait_top_height.GetValue()
|
||||
: Settings::values.custom_top_height.GetValue();
|
||||
const u16 bottom_x = is_portrait_mode ? Settings::values.custom_portrait_bottom_x.GetValue()
|
||||
: Settings::values.custom_bottom_x.GetValue();
|
||||
const u16 bottom_width = is_portrait_mode
|
||||
? Settings::values.custom_portrait_bottom_width.GetValue()
|
||||
: Settings::values.custom_bottom_width.GetValue();
|
||||
const u16 bottom_y = is_portrait_mode ? Settings::values.custom_portrait_bottom_y.GetValue()
|
||||
: Settings::values.custom_bottom_y.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),
|
||||
(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.bottom_screen = bot_screen;
|
||||
}
|
||||
return res;
|
||||
if (upright) {
|
||||
return reverseLayout(res);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
case Settings::LayoutOption::LargeScreen:
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width =
|
||||
(Core::kScreenBottomWidth +
|
||||
Core::kScreenTopWidth /
|
||||
static_cast<int>(Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
case Settings::LayoutOption::LargeScreen: {
|
||||
const bool swapped = Settings::values.swap_screen.GetValue();
|
||||
const int largeWidth = swapped ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
||||
const int largeHeight = swapped ? Core::kScreenBottomHeight : Core::kScreenTopHeight;
|
||||
const int smallWidth =
|
||||
static_cast<int>((swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth) /
|
||||
Settings::values.large_screen_proportion.GetValue());
|
||||
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 {
|
||||
width =
|
||||
(Core::kScreenTopWidth +
|
||||
Core::kScreenBottomWidth /
|
||||
static_cast<int>(Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
width = (largeWidth + smallWidth) * res_scale;
|
||||
height = std::max(largeHeight, smallHeight) * res_scale;
|
||||
}
|
||||
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(),
|
||||
Settings::values.large_screen_proportion.GetValue(),
|
||||
VerticalAlignment::Bottom);
|
||||
|
||||
Settings::values.small_screen_position.GetValue());
|
||||
}
|
||||
case Settings::LayoutOption::SideScreen:
|
||||
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * 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(),
|
||||
Settings::values.upright_screen.GetValue(), 1,
|
||||
VerticalAlignment::Middle);
|
||||
Settings::SmallScreenPosition::MiddleRight);
|
||||
|
||||
case Settings::LayoutOption::Default:
|
||||
default:
|
||||
@ -613,10 +542,44 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& 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() {
|
||||
u32 min_width, min_height;
|
||||
min_width = Core::kScreenTopWidth;
|
||||
min_height = Core::kScreenTopHeight + Core::kScreenBottomHeight;
|
||||
const u32 min_width = Core::kScreenTopWidth;
|
||||
const u32 min_height = Core::kScreenTopHeight + Core::kScreenBottomHeight;
|
||||
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_height = Core::kScreenBottomHeight;
|
||||
break;
|
||||
case Settings::LayoutOption::LargeScreen:
|
||||
min_width = static_cast<u32>(
|
||||
Settings::values.swap_screen
|
||||
? Core::kScreenTopWidth / Settings::values.large_screen_proportion.GetValue() +
|
||||
Core::kScreenBottomWidth
|
||||
: Core::kScreenTopWidth + Core::kScreenBottomWidth /
|
||||
Settings::values.large_screen_proportion.GetValue());
|
||||
min_height = Core::kScreenBottomHeight;
|
||||
case Settings::LayoutOption::LargeScreen: {
|
||||
const bool swapped = Settings::values.swap_screen.GetValue();
|
||||
const int largeWidth = swapped ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
||||
const int largeHeight = swapped ? Core::kScreenBottomHeight : Core::kScreenTopHeight;
|
||||
int smallWidth = swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth;
|
||||
int smallHeight = swapped ? Core::kScreenTopHeight : Core::kScreenBottomHeight;
|
||||
smallWidth =
|
||||
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;
|
||||
}
|
||||
case Settings::LayoutOption::SideScreen:
|
||||
min_width = Core::kScreenTopWidth + Core::kScreenBottomWidth;
|
||||
min_height = Core::kScreenBottomHeight;
|
||||
|
@ -5,10 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/math_util.h"
|
||||
|
||||
namespace Settings {
|
||||
enum class LayoutOption : u32;
|
||||
}
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
@ -20,31 +17,6 @@ enum class DisplayOrientation {
|
||||
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
|
||||
struct CardboardSettings {
|
||||
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 height Window framebuffer height in pixels
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Factory method for constructing a Frame with the a 4x size Top screen with a 1x size bottom
|
||||
* screen on the right
|
||||
* This is useful in particular because it matches well with a 1920x1080 resolution monitor
|
||||
* Factory method for constructing a Frame with differently sized top and bottom windows
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @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
|
||||
*/
|
||||
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,
|
||||
* 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 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 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
|
||||
*/
|
||||
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
|
||||
// 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) {
|
||||
// Note: the following default values are verified against real 3DS
|
||||
ContextConfig& context = camera.contexts[context_id];
|
||||
context.flip = camera_id == 1 ? Flip::Horizontal : Flip::None;
|
||||
context.effect = Effect::None;
|
||||
context.flip = camera_id == 1 ? Flip::Horizontal : Flip::NoFlip;
|
||||
context.effect = Effect::NoEffect;
|
||||
context.format = OutputFormat::YUV422;
|
||||
context.resolution =
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -656,8 +656,8 @@ private:
|
||||
Result SetPackageParameter(const PackageParameterType& package);
|
||||
|
||||
struct ContextConfig {
|
||||
Flip flip{Flip::None};
|
||||
Effect effect{Effect::None};
|
||||
Flip flip{Flip::NoFlip};
|
||||
Effect effect{Effect::NoEffect};
|
||||
OutputFormat format{OutputFormat::YUV422};
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -17,7 +17,7 @@ enum CameraIndex {
|
||||
};
|
||||
|
||||
enum class Effect : u8 {
|
||||
None = 0,
|
||||
NoEffect = 0,
|
||||
Mono = 1,
|
||||
Sepia = 2,
|
||||
Negative = 3,
|
||||
@ -26,7 +26,7 @@ enum class Effect : u8 {
|
||||
};
|
||||
|
||||
enum class Flip : u8 {
|
||||
None = 0,
|
||||
NoFlip = 0,
|
||||
Horizontal = 1,
|
||||
Vertical = 2,
|
||||
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
|
||||
// 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) {
|
||||
if (effect != Service::CAM::Effect::None) {
|
||||
if (effect != Service::CAM::Effect::NoEffect) {
|
||||
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.upright_screen);
|
||||
ReadGlobalSetting(Settings::values.large_screen_proportion);
|
||||
ReadGlobalSetting(Settings::values.small_screen_position);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.mono_render_option);
|
||||
@ -1077,7 +1078,7 @@ void QtConfig::SaveLayoutValues() {
|
||||
WriteGlobalSetting(Settings::values.swap_screen);
|
||||
WriteGlobalSetting(Settings::values.upright_screen);
|
||||
WriteGlobalSetting(Settings::values.large_screen_proportion);
|
||||
|
||||
WriteGlobalSetting(Settings::values.small_screen_position);
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.mono_render_option);
|
||||
WriteBasicSetting(Settings::values.custom_top_x);
|
||||
|
@ -206,8 +206,8 @@ void ConfigureCamera::StartPreviewing() {
|
||||
}
|
||||
previewing_camera->SetResolution(
|
||||
{static_cast<u16>(preview_width), static_cast<u16>(preview_height)});
|
||||
previewing_camera->SetEffect(Service::CAM::Effect::None);
|
||||
previewing_camera->SetFlip(Service::CAM::Flip::None);
|
||||
previewing_camera->SetEffect(Service::CAM::Effect::NoEffect);
|
||||
previewing_camera->SetFlip(Service::CAM::Flip::NoFlip);
|
||||
previewing_camera->SetFormat(Service::CAM::OutputFormat::RGB565);
|
||||
previewing_camera->SetFrameRate(Service::CAM::FrameRate::Rate_30);
|
||||
previewing_camera->StartCapture();
|
||||
|
@ -28,6 +28,15 @@ ConfigureLayout::ConfigureLayout(QWidget* parent)
|
||||
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(
|
||||
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SingleScreen) ||
|
||||
(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_upright_screen->setChecked(Settings::values.upright_screen.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_y->setValue(Settings::values.custom_top_y.GetValue());
|
||||
ui->custom_top_width->setValue(Settings::values.custom_top_width.GetValue());
|
||||
@ -153,7 +163,8 @@ void ConfigureLayout::RetranslateUI() {
|
||||
|
||||
void ConfigureLayout::ApplyConfiguration() {
|
||||
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_y = ui->custom_top_y->value();
|
||||
Settings::values.custom_top_width = ui->custom_top_width->value();
|
||||
|
@ -145,6 +145,75 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
<widget class="QWidget" name="bg_color_group" native="true">
|
||||
<layout class="QHBoxLayout" name="bg_color_group_2">
|
||||
@ -592,6 +661,7 @@
|
||||
<tabstop>toggle_swap_screen</tabstop>
|
||||
<tabstop>toggle_upright_screen</tabstop>
|
||||
<tabstop>large_screen_proportion</tabstop>
|
||||
<tabstop>small_screen_position_combobox</tabstop>
|
||||
<tabstop>bg_button</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
@ -580,6 +580,16 @@ void GMainWindow::InitializeWidgets() {
|
||||
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_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() {
|
||||
@ -1032,6 +1042,14 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
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_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
|
||||
connect_menu(ui->action_Record_Movie, &GMainWindow::OnRecordMovie);
|
||||
@ -2548,13 +2566,13 @@ void GMainWindow::UpdateSecondaryWindowVisibility() {
|
||||
|
||||
void GMainWindow::ChangeScreenLayout() {
|
||||
Settings::LayoutOption new_layout = Settings::LayoutOption::Default;
|
||||
|
||||
if (ui->action_Screen_Layout_Default->isChecked()) {
|
||||
new_layout = Settings::LayoutOption::Default;
|
||||
} else if (ui->action_Screen_Layout_Single_Screen->isChecked()) {
|
||||
new_layout = Settings::LayoutOption::SingleScreen;
|
||||
} else if (ui->action_Screen_Layout_Large_Screen->isChecked()) {
|
||||
new_layout = Settings::LayoutOption::LargeScreen;
|
||||
ui->menu_Small_Screen_Position->setEnabled(true);
|
||||
} else if (ui->action_Screen_Layout_Hybrid_Screen->isChecked()) {
|
||||
new_layout = Settings::LayoutOption::HybridScreen;
|
||||
} else if (ui->action_Screen_Layout_Side_by_Side->isChecked()) {
|
||||
@ -2566,6 +2584,34 @@ void GMainWindow::ChangeScreenLayout() {
|
||||
}
|
||||
|
||||
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();
|
||||
UpdateSecondaryWindowVisibility();
|
||||
}
|
||||
@ -3637,6 +3683,31 @@ void GMainWindow::SyncMenuUISettings() {
|
||||
ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen.GetValue());
|
||||
ui->action_Screen_Layout_Upright_Screens->setChecked(
|
||||
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() {
|
||||
|
@ -265,6 +265,7 @@ private slots:
|
||||
void ToggleFullscreen();
|
||||
void ToggleSecondaryFullscreen();
|
||||
void ChangeScreenLayout();
|
||||
void ChangeSmallScreenPosition();
|
||||
void UpdateSecondaryWindowVisibility();
|
||||
void ToggleScreenLayout();
|
||||
void OnSwapScreens();
|
||||
|
@ -142,6 +142,23 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Screen_Layout_Upright_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>
|
||||
<addaction name="action_Fullscreen"/>
|
||||
<addaction name="action_Single_Window_Mode"/>
|
||||
@ -541,6 +558,70 @@
|
||||
<string>Custom Layout</string>
|
||||
</property>
|
||||
</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">
|
||||
<property name="checkable">
|
||||
<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
|
||||
// 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.is_tiled = true;
|
||||
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();
|
||||
|
||||
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
|
||||
// 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 bool is_depth =
|
||||
surface.type == SurfaceType::Depth || surface.type == SurfaceType::DepthStencil;
|
||||
if (filter == Settings::TextureFilter::None || is_depth) {
|
||||
if (filter == Settings::TextureFilter::NoFilter || is_depth) {
|
||||
return false;
|
||||
}
|
||||
if (blit.src_level != 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user