editable Overlay Button (position and opacity)

This commit is contained in:
BeZide93 2025-10-06 11:49:55 -05:00 committed by KeatonTheBot
parent 7a5b5dee6a
commit 4d1620c330
3 changed files with 169 additions and 60 deletions

View file

@ -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)

View file

@ -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")
} }

View file

@ -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