mirror of
https://git.ryujinx.app/kenji-nx/ryujinx.git
synced 2025-12-15 19:37:05 +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
|
||||
|
||||
// --- Overlay Settings
|
||||
var overlayMenuPosition: OverlayMenuPosition
|
||||
var overlayMenuOpacity: Float
|
||||
|
||||
var ignoreMissingServices: Boolean
|
||||
var enablePptc: Boolean
|
||||
var enableLowPowerPptc: Boolean
|
||||
|
|
@ -61,6 +70,12 @@ class QuickSettings(val activity: Activity) {
|
|||
val oriValue = sharedPref.getInt("orientationPreference", ActivityInfo.SCREEN_ORIENTATION_SENSOR)
|
||||
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)]
|
||||
useNce = sharedPref.getBoolean("useNce", false)
|
||||
memoryConfiguration = MemoryConfiguration.entries.toTypedArray()[sharedPref.getInt("memoryConfiguration", MemoryConfiguration.MemoryConfiguration4GiB.ordinal)]
|
||||
|
|
@ -100,6 +115,10 @@ class QuickSettings(val activity: Activity) {
|
|||
// --- Save orientation
|
||||
putInt("orientationPreference", orientationPreference.value)
|
||||
|
||||
// --- NEU: Overlay Settings speichern
|
||||
putInt("overlayMenuPosition", overlayMenuPosition.ordinal)
|
||||
putFloat("overlayMenuOpacity", overlayMenuOpacity.coerceIn(0f, 1f))
|
||||
|
||||
putInt("memoryManagerMode", memoryManagerMode.ordinal)
|
||||
putBoolean("useNce", useNce)
|
||||
putInt("memoryConfiguration", memoryConfiguration.ordinal)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.draw.alpha // ← NEU
|
||||
import compose.icons.CssGgIcons
|
||||
import compose.icons.cssggicons.ToolbarBottom
|
||||
import org.kenjinx.android.GameController
|
||||
|
|
@ -75,31 +76,36 @@ class GameViews {
|
|||
@Composable
|
||||
fun GameOverlay(mainViewModel: MainViewModel) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
val showStats = remember {
|
||||
mutableStateOf(false)
|
||||
val showStats = remember { 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 {
|
||||
mutableStateOf(QuickSettings(mainViewModel.activity).useVirtualController)
|
||||
val overlayOpacityState = remember {
|
||||
mutableStateOf(QuickSettings(mainViewModel.activity).overlayMenuOpacity.coerceIn(0f, 1f))
|
||||
}
|
||||
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")
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (showStats.value) {
|
||||
GameStats(mainViewModel)
|
||||
}
|
||||
|
|
@ -130,18 +136,14 @@ class GameViews {
|
|||
position.y.roundToInt()
|
||||
)
|
||||
}
|
||||
|
||||
PointerEventType.Release -> {
|
||||
KenjinxNative.inputReleaseTouchPoint()
|
||||
|
||||
}
|
||||
|
||||
PointerEventType.Move -> {
|
||||
KenjinxNative.inputSetTouchPoint(
|
||||
position.x.roundToInt(),
|
||||
position.y.roundToInt()
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,13 +151,16 @@ class GameViews {
|
|||
}
|
||||
}) {
|
||||
}
|
||||
|
||||
if (!showLoading.value) {
|
||||
GameController.Compose(mainViewModel)
|
||||
|
||||
// --- Button at any corner/edge + transparency
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.align(overlayAlignment())
|
||||
.padding(8.dp)
|
||||
.alpha(overlayOpacityState.value) // 0f = unsichtbar, aber weiter klickbar
|
||||
) {
|
||||
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
||||
showMore.value = true
|
||||
|
|
@ -169,8 +174,9 @@ class GameViews {
|
|||
|
||||
if (showMore.value) {
|
||||
Popup(
|
||||
alignment = Alignment.BottomCenter,
|
||||
onDismissRequest = { showMore.value = false }) {
|
||||
alignment = overlayAlignment(), // --- NEU: Panel an gleicher Position
|
||||
onDismissRequest = { showMore.value = false }
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
shape = MaterialTheme.shapes.medium
|
||||
|
|
@ -194,12 +200,9 @@ class GameViews {
|
|||
}
|
||||
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
||||
showMore.value = false
|
||||
if(vSyncMode.value == VSyncMode.Switch)
|
||||
{
|
||||
if(vSyncMode.value == VSyncMode.Switch) {
|
||||
vSyncMode.value= VSyncMode.Unbounded
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
vSyncMode.value= VSyncMode.Switch
|
||||
}
|
||||
KenjinxNative.graphicsRendererSetVsync(
|
||||
|
|
@ -246,17 +249,14 @@ class GameViews {
|
|||
}
|
||||
}
|
||||
|
||||
val showBackNotice = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val showBackNotice = remember { 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
|
||||
BackHandler(enabled = uiHandler.showMessage.value) {
|
||||
KenjinxNative.uiHandlerSetResponse(false, "")
|
||||
uiHandler.showMessage.value = false
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
showBackNotice.value = true
|
||||
}
|
||||
|
|
@ -286,24 +286,12 @@ class GameViews {
|
|||
|
||||
@Composable
|
||||
fun GameStats(mainViewModel: MainViewModel) {
|
||||
val fifo = remember {
|
||||
mutableDoubleStateOf(0.0)
|
||||
}
|
||||
val gameFps = remember {
|
||||
mutableDoubleStateOf(0.0)
|
||||
}
|
||||
val gameTime = remember {
|
||||
mutableDoubleStateOf(0.0)
|
||||
}
|
||||
val usedMem = remember {
|
||||
mutableIntStateOf(0)
|
||||
}
|
||||
val totalMem = remember {
|
||||
mutableIntStateOf(0)
|
||||
}
|
||||
val frequencies = remember {
|
||||
mutableListOf<Double>()
|
||||
}
|
||||
val fifo = remember { mutableDoubleStateOf(0.0) }
|
||||
val gameFps = remember { mutableDoubleStateOf(0.0) }
|
||||
val gameTime = remember { mutableDoubleStateOf(0.0) }
|
||||
val usedMem = remember { mutableIntStateOf(0) }
|
||||
val totalMem = remember { mutableIntStateOf(0) }
|
||||
val frequencies = remember { mutableListOf<Double>() }
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
|
|
@ -324,10 +312,7 @@ class GameViews {
|
|||
if (i < frequencies.size) {
|
||||
val t = frequencies[i]
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier.padding(2.dp),
|
||||
text = "CPU $i"
|
||||
)
|
||||
Text(modifier = Modifier.padding(2.dp), text = "CPU $i")
|
||||
Spacer(Modifier.weight(1f))
|
||||
Text(text = "$t MHz")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ import org.kenjinx.android.widgets.SwitchSelector
|
|||
// >>> QuickSettings + OrientationPreference
|
||||
import org.kenjinx.android.viewmodels.QuickSettings
|
||||
import org.kenjinx.android.viewmodels.QuickSettings.OrientationPreference
|
||||
import org.kenjinx.android.viewmodels.QuickSettings.OverlayMenuPosition // ← NEU
|
||||
|
||||
// Import enums
|
||||
import org.kenjinx.android.SystemLanguage
|
||||
|
|
@ -150,6 +151,14 @@ class SettingViews {
|
|||
val systemLanguage = remember { mutableStateOf(SystemLanguage.AmericanEnglish) }
|
||||
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) {
|
||||
settingsViewModel.initializeState(
|
||||
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")
|
||||
Row(
|
||||
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 ----
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue