mirror of
https://git.ryujinx.app/kenji-nx/ryujinx.git
synced 2025-12-16 22:37:06 +00:00
editable Overlay Button (position and opacity)
This commit is contained in:
parent
7a5b5dee6a
commit
4d1620c330
3 changed files with 169 additions and 60 deletions
|
|
@ -19,8 +19,17 @@ class QuickSettings(val activity: Activity) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Overlay Position
|
||||||
|
enum class OverlayMenuPosition {
|
||||||
|
BottomMiddle, BottomLeft, BottomRight, TopMiddle, TopLeft, TopRight
|
||||||
|
}
|
||||||
|
|
||||||
var orientationPreference: OrientationPreference
|
var orientationPreference: OrientationPreference
|
||||||
|
|
||||||
|
// --- Overlay Settings
|
||||||
|
var overlayMenuPosition: OverlayMenuPosition
|
||||||
|
var overlayMenuOpacity: Float
|
||||||
|
|
||||||
var ignoreMissingServices: Boolean
|
var ignoreMissingServices: Boolean
|
||||||
var enablePptc: Boolean
|
var enablePptc: Boolean
|
||||||
var enableLowPowerPptc: Boolean
|
var enableLowPowerPptc: Boolean
|
||||||
|
|
@ -61,6 +70,12 @@ class QuickSettings(val activity: Activity) {
|
||||||
val oriValue = sharedPref.getInt("orientationPreference", ActivityInfo.SCREEN_ORIENTATION_SENSOR)
|
val oriValue = sharedPref.getInt("orientationPreference", ActivityInfo.SCREEN_ORIENTATION_SENSOR)
|
||||||
orientationPreference = OrientationPreference.fromValue(oriValue)
|
orientationPreference = OrientationPreference.fromValue(oriValue)
|
||||||
|
|
||||||
|
// --- NEU: Overlay Settings laden
|
||||||
|
overlayMenuPosition = OverlayMenuPosition.entries[
|
||||||
|
sharedPref.getInt("overlayMenuPosition", OverlayMenuPosition.BottomMiddle.ordinal)
|
||||||
|
]
|
||||||
|
overlayMenuOpacity = sharedPref.getFloat("overlayMenuOpacity", 1f).coerceIn(0f, 1f)
|
||||||
|
|
||||||
memoryManagerMode = MemoryManagerMode.entries.toTypedArray()[sharedPref.getInt("memoryManagerMode", MemoryManagerMode.HostMappedUnsafe.ordinal)]
|
memoryManagerMode = MemoryManagerMode.entries.toTypedArray()[sharedPref.getInt("memoryManagerMode", MemoryManagerMode.HostMappedUnsafe.ordinal)]
|
||||||
useNce = sharedPref.getBoolean("useNce", false)
|
useNce = sharedPref.getBoolean("useNce", false)
|
||||||
memoryConfiguration = MemoryConfiguration.entries.toTypedArray()[sharedPref.getInt("memoryConfiguration", MemoryConfiguration.MemoryConfiguration4GiB.ordinal)]
|
memoryConfiguration = MemoryConfiguration.entries.toTypedArray()[sharedPref.getInt("memoryConfiguration", MemoryConfiguration.MemoryConfiguration4GiB.ordinal)]
|
||||||
|
|
@ -100,6 +115,10 @@ class QuickSettings(val activity: Activity) {
|
||||||
// --- Save orientation
|
// --- Save orientation
|
||||||
putInt("orientationPreference", orientationPreference.value)
|
putInt("orientationPreference", orientationPreference.value)
|
||||||
|
|
||||||
|
// --- NEU: Overlay Settings speichern
|
||||||
|
putInt("overlayMenuPosition", overlayMenuPosition.ordinal)
|
||||||
|
putFloat("overlayMenuOpacity", overlayMenuOpacity.coerceIn(0f, 1f))
|
||||||
|
|
||||||
putInt("memoryManagerMode", memoryManagerMode.ordinal)
|
putInt("memoryManagerMode", memoryManagerMode.ordinal)
|
||||||
putBoolean("useNce", useNce)
|
putBoolean("useNce", useNce)
|
||||||
putInt("memoryConfiguration", memoryConfiguration.ordinal)
|
putInt("memoryConfiguration", memoryConfiguration.ordinal)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
|
import androidx.compose.ui.draw.alpha // ← NEU
|
||||||
import compose.icons.CssGgIcons
|
import compose.icons.CssGgIcons
|
||||||
import compose.icons.cssggicons.ToolbarBottom
|
import compose.icons.cssggicons.ToolbarBottom
|
||||||
import org.kenjinx.android.GameController
|
import org.kenjinx.android.GameController
|
||||||
|
|
@ -75,31 +76,36 @@ class GameViews {
|
||||||
@Composable
|
@Composable
|
||||||
fun GameOverlay(mainViewModel: MainViewModel) {
|
fun GameOverlay(mainViewModel: MainViewModel) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
val showStats = remember {
|
val showStats = remember { mutableStateOf(false) }
|
||||||
mutableStateOf(false)
|
val showController = remember { mutableStateOf(QuickSettings(mainViewModel.activity).useVirtualController) }
|
||||||
|
val vSyncMode = remember { mutableStateOf(QuickSettings(mainViewModel.activity).vSyncMode) }
|
||||||
|
val enableMotion = remember { mutableStateOf(QuickSettings(mainViewModel.activity).enableMotion) }
|
||||||
|
val showMore = remember { mutableStateOf(false) }
|
||||||
|
val showLoading = remember { mutableStateOf(true) }
|
||||||
|
val progressValue = remember { mutableStateOf(0.0f) }
|
||||||
|
val progress = remember { mutableStateOf("Loading") }
|
||||||
|
|
||||||
|
// --- Read overlay settings
|
||||||
|
val overlayPositionState = remember {
|
||||||
|
mutableStateOf(QuickSettings(mainViewModel.activity).overlayMenuPosition)
|
||||||
}
|
}
|
||||||
val showController = remember {
|
val overlayOpacityState = remember {
|
||||||
mutableStateOf(QuickSettings(mainViewModel.activity).useVirtualController)
|
mutableStateOf(QuickSettings(mainViewModel.activity).overlayMenuOpacity.coerceIn(0f, 1f))
|
||||||
}
|
}
|
||||||
val vSyncMode = remember {
|
|
||||||
mutableStateOf(QuickSettings(mainViewModel.activity).vSyncMode)
|
// Auxiliary mapping position → alignment
|
||||||
|
fun overlayAlignment(): Alignment {
|
||||||
|
return when (overlayPositionState.value) {
|
||||||
|
QuickSettings.OverlayMenuPosition.BottomMiddle -> Alignment.BottomCenter
|
||||||
|
QuickSettings.OverlayMenuPosition.BottomLeft -> Alignment.BottomStart
|
||||||
|
QuickSettings.OverlayMenuPosition.BottomRight -> Alignment.BottomEnd
|
||||||
|
QuickSettings.OverlayMenuPosition.TopMiddle -> Alignment.TopCenter
|
||||||
|
QuickSettings.OverlayMenuPosition.TopLeft -> Alignment.TopStart
|
||||||
|
QuickSettings.OverlayMenuPosition.TopRight -> Alignment.TopEnd
|
||||||
}
|
}
|
||||||
val enableMotion = remember {
|
|
||||||
mutableStateOf(QuickSettings(mainViewModel.activity).enableMotion)
|
|
||||||
}
|
|
||||||
val showMore = remember {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
val showLoading = remember {
|
|
||||||
mutableStateOf(true)
|
|
||||||
}
|
|
||||||
val progressValue = remember {
|
|
||||||
mutableStateOf(0.0f)
|
|
||||||
}
|
|
||||||
val progress = remember {
|
|
||||||
mutableStateOf("Loading")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (showStats.value) {
|
if (showStats.value) {
|
||||||
GameStats(mainViewModel)
|
GameStats(mainViewModel)
|
||||||
}
|
}
|
||||||
|
|
@ -130,18 +136,14 @@ class GameViews {
|
||||||
position.y.roundToInt()
|
position.y.roundToInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PointerEventType.Release -> {
|
PointerEventType.Release -> {
|
||||||
KenjinxNative.inputReleaseTouchPoint()
|
KenjinxNative.inputReleaseTouchPoint()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PointerEventType.Move -> {
|
PointerEventType.Move -> {
|
||||||
KenjinxNative.inputSetTouchPoint(
|
KenjinxNative.inputSetTouchPoint(
|
||||||
position.x.roundToInt(),
|
position.x.roundToInt(),
|
||||||
position.y.roundToInt()
|
position.y.roundToInt()
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -149,13 +151,16 @@ class GameViews {
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showLoading.value) {
|
if (!showLoading.value) {
|
||||||
GameController.Compose(mainViewModel)
|
GameController.Compose(mainViewModel)
|
||||||
|
|
||||||
|
// --- Button at any corner/edge + transparency
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(overlayAlignment())
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
|
.alpha(overlayOpacityState.value) // 0f = unsichtbar, aber weiter klickbar
|
||||||
) {
|
) {
|
||||||
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
||||||
showMore.value = true
|
showMore.value = true
|
||||||
|
|
@ -169,8 +174,9 @@ class GameViews {
|
||||||
|
|
||||||
if (showMore.value) {
|
if (showMore.value) {
|
||||||
Popup(
|
Popup(
|
||||||
alignment = Alignment.BottomCenter,
|
alignment = overlayAlignment(), // --- NEU: Panel an gleicher Position
|
||||||
onDismissRequest = { showMore.value = false }) {
|
onDismissRequest = { showMore.value = false }
|
||||||
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
shape = MaterialTheme.shapes.medium
|
shape = MaterialTheme.shapes.medium
|
||||||
|
|
@ -194,12 +200,9 @@ class GameViews {
|
||||||
}
|
}
|
||||||
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
||||||
showMore.value = false
|
showMore.value = false
|
||||||
if(vSyncMode.value == VSyncMode.Switch)
|
if(vSyncMode.value == VSyncMode.Switch) {
|
||||||
{
|
|
||||||
vSyncMode.value= VSyncMode.Unbounded
|
vSyncMode.value= VSyncMode.Unbounded
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
vSyncMode.value= VSyncMode.Switch
|
vSyncMode.value= VSyncMode.Switch
|
||||||
}
|
}
|
||||||
KenjinxNative.graphicsRendererSetVsync(
|
KenjinxNative.graphicsRendererSetVsync(
|
||||||
|
|
@ -246,17 +249,14 @@ class GameViews {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val showBackNotice = remember {
|
val showBackNotice = remember { mutableStateOf(false) }
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEW: If the software keyboard is open, catch Back and close ONLY the dialog.
|
// If the software keyboard is open, catch Back and close ONLY the dialog.
|
||||||
val uiHandler = mainViewModel.activity.uiHandler
|
val uiHandler = mainViewModel.activity.uiHandler
|
||||||
BackHandler(enabled = uiHandler.showMessage.value) {
|
BackHandler(enabled = uiHandler.showMessage.value) {
|
||||||
KenjinxNative.uiHandlerSetResponse(false, "")
|
KenjinxNative.uiHandlerSetResponse(false, "")
|
||||||
uiHandler.showMessage.value = false
|
uiHandler.showMessage.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
BackHandler {
|
BackHandler {
|
||||||
showBackNotice.value = true
|
showBackNotice.value = true
|
||||||
}
|
}
|
||||||
|
|
@ -286,24 +286,12 @@ class GameViews {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GameStats(mainViewModel: MainViewModel) {
|
fun GameStats(mainViewModel: MainViewModel) {
|
||||||
val fifo = remember {
|
val fifo = remember { mutableDoubleStateOf(0.0) }
|
||||||
mutableDoubleStateOf(0.0)
|
val gameFps = remember { mutableDoubleStateOf(0.0) }
|
||||||
}
|
val gameTime = remember { mutableDoubleStateOf(0.0) }
|
||||||
val gameFps = remember {
|
val usedMem = remember { mutableIntStateOf(0) }
|
||||||
mutableDoubleStateOf(0.0)
|
val totalMem = remember { mutableIntStateOf(0) }
|
||||||
}
|
val frequencies = remember { mutableListOf<Double>() }
|
||||||
val gameTime = remember {
|
|
||||||
mutableDoubleStateOf(0.0)
|
|
||||||
}
|
|
||||||
val usedMem = remember {
|
|
||||||
mutableIntStateOf(0)
|
|
||||||
}
|
|
||||||
val totalMem = remember {
|
|
||||||
mutableIntStateOf(0)
|
|
||||||
}
|
|
||||||
val frequencies = remember {
|
|
||||||
mutableListOf<Double>()
|
|
||||||
}
|
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
|
|
@ -324,10 +312,7 @@ class GameViews {
|
||||||
if (i < frequencies.size) {
|
if (i < frequencies.size) {
|
||||||
val t = frequencies[i]
|
val t = frequencies[i]
|
||||||
Row {
|
Row {
|
||||||
Text(
|
Text(modifier = Modifier.padding(2.dp), text = "CPU $i")
|
||||||
modifier = Modifier.padding(2.dp),
|
|
||||||
text = "CPU $i"
|
|
||||||
)
|
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
Text(text = "$t MHz")
|
Text(text = "$t MHz")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ import org.kenjinx.android.widgets.SwitchSelector
|
||||||
// >>> QuickSettings + OrientationPreference
|
// >>> QuickSettings + OrientationPreference
|
||||||
import org.kenjinx.android.viewmodels.QuickSettings
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
import org.kenjinx.android.viewmodels.QuickSettings.OrientationPreference
|
import org.kenjinx.android.viewmodels.QuickSettings.OrientationPreference
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings.OverlayMenuPosition // ← NEU
|
||||||
|
|
||||||
// Import enums
|
// Import enums
|
||||||
import org.kenjinx.android.SystemLanguage
|
import org.kenjinx.android.SystemLanguage
|
||||||
|
|
@ -150,6 +151,14 @@ class SettingViews {
|
||||||
val systemLanguage = remember { mutableStateOf(SystemLanguage.AmericanEnglish) }
|
val systemLanguage = remember { mutableStateOf(SystemLanguage.AmericanEnglish) }
|
||||||
val regionCode = remember { mutableStateOf(RegionCode.USA) }
|
val regionCode = remember { mutableStateOf(RegionCode.USA) }
|
||||||
|
|
||||||
|
// Load overlay settings from QuickSettings
|
||||||
|
val overlayMenuPosition = remember {
|
||||||
|
mutableStateOf(QuickSettings(mainViewModel.activity).overlayMenuPosition)
|
||||||
|
}
|
||||||
|
val overlayOpacity = remember {
|
||||||
|
mutableFloatStateOf(QuickSettings(mainViewModel.activity).overlayMenuOpacity.coerceIn(0f, 1f))
|
||||||
|
}
|
||||||
|
|
||||||
if (!loaded.value) {
|
if (!loaded.value) {
|
||||||
settingsViewModel.initializeState(
|
settingsViewModel.initializeState(
|
||||||
memoryManagerMode,
|
memoryManagerMode,
|
||||||
|
|
@ -281,6 +290,69 @@ class SettingViews {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Overlay Menu Position (DropdownSelector as usual)
|
||||||
|
OverlayPositionDropdown(
|
||||||
|
selectedPosition = overlayMenuPosition.value,
|
||||||
|
onPositionSelected = { pos ->
|
||||||
|
overlayMenuPosition.value = pos
|
||||||
|
val qs = QuickSettings(mainViewModel.activity)
|
||||||
|
qs.overlayMenuPosition = pos
|
||||||
|
qs.save()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Overlay transparency slider – identical style as controller stick sensitivity
|
||||||
|
val interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(56.dp)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Overlay transparency",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Slider(
|
||||||
|
modifier = Modifier.width(250.dp),
|
||||||
|
value = overlayOpacity.floatValue,
|
||||||
|
onValueChange = {
|
||||||
|
val clamped = it.coerceIn(0f, 1f)
|
||||||
|
overlayOpacity.floatValue = clamped
|
||||||
|
val qs = QuickSettings(mainViewModel.activity)
|
||||||
|
qs.overlayMenuOpacity = clamped
|
||||||
|
qs.save()
|
||||||
|
},
|
||||||
|
valueRange = 0f..1f,
|
||||||
|
steps = 20,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
thumb = {
|
||||||
|
Label(
|
||||||
|
label = {
|
||||||
|
PlainTooltip(
|
||||||
|
modifier = Modifier
|
||||||
|
.sizeIn(45.dp, 25.dp)
|
||||||
|
.wrapContentWidth()
|
||||||
|
) {
|
||||||
|
Text("${(overlayOpacity.floatValue * 100f).toInt()}%")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interactionSource = interactionSource
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = org.kenjinx.android.Icons.circle(
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
isGrid.SwitchSelector("Use Grid")
|
isGrid.SwitchSelector("Use Grid")
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -1335,6 +1407,39 @@ class SettingViews {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Overlay position dropdown ----
|
||||||
|
@Composable
|
||||||
|
fun OverlayPositionDropdown(
|
||||||
|
selectedPosition: OverlayMenuPosition,
|
||||||
|
onPositionSelected: (OverlayMenuPosition) -> Unit
|
||||||
|
) {
|
||||||
|
val options = listOf(
|
||||||
|
OverlayMenuPosition.BottomMiddle,
|
||||||
|
OverlayMenuPosition.BottomLeft,
|
||||||
|
OverlayMenuPosition.BottomRight,
|
||||||
|
OverlayMenuPosition.TopMiddle,
|
||||||
|
OverlayMenuPosition.TopLeft,
|
||||||
|
OverlayMenuPosition.TopRight
|
||||||
|
)
|
||||||
|
|
||||||
|
DropdownSelector(
|
||||||
|
label = "Overlay Menu Position",
|
||||||
|
selectedValue = selectedPosition,
|
||||||
|
options = options,
|
||||||
|
getDisplayText = { opt ->
|
||||||
|
when (opt) {
|
||||||
|
OverlayMenuPosition.BottomMiddle -> "bottom middle"
|
||||||
|
OverlayMenuPosition.BottomLeft -> "bottom left"
|
||||||
|
OverlayMenuPosition.BottomRight -> "bottom right"
|
||||||
|
OverlayMenuPosition.TopMiddle -> "top middle"
|
||||||
|
OverlayMenuPosition.TopLeft -> "top left"
|
||||||
|
OverlayMenuPosition.TopRight -> "top right"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onOptionSelected = onPositionSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Dropdowns for language & region ----
|
// ---- Dropdowns for language & region ----
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue