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

View file

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

View file

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