add zoom control

This commit is contained in:
Chloe committed 2025-01-19 01:25:49 -07:00
1 parent 10a0670a47
commit 327e9b4d39
6 files changed
+192 -12

No files matched your search

@@ -68,6 +68,8 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
private var displayMode = DisplayMode.DEFAULT private var displayMode = DisplayMode.DEFAULT
private var forceRefreshRate = false private var forceRefreshRate = false
private var mAspectRatio = 0.0f private var mAspectRatio = 0.0f
private var mScreenZoom = 1.0f
private var mScreenZoomFit = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setupUIState() setupUIState()
@@ -379,6 +381,15 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
frameLayout.aspectRatio = mAspectRatio frameLayout.aspectRatio = mAspectRatio
} }
if (mScreenZoomFit && mScreenZoom != 1.0f) {
frameLayout.scaleX = 1/mScreenZoom
frameLayout.scaleY = 1/mScreenZoom
}
if (mScreenZoom != 1.0f) {
frameLayout.zoom = mScreenZoom
}
val editTextLayoutParams = ViewGroup.LayoutParams( val editTextLayoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
@@ -434,6 +445,8 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
) )
mAspectRatio = ratioForPreference(preferenceUtils.getString(PreferenceUtils.Key.CUSTOM_ASPECT_RATIO) ?: "16_9") mAspectRatio = ratioForPreference(preferenceUtils.getString(PreferenceUtils.Key.CUSTOM_ASPECT_RATIO) ?: "16_9")
mScreenZoom = preferenceUtils.getInt(PreferenceUtils.Key.SCREEN_ZOOM) / 100.0f
mScreenZoomFit = preferenceUtils.getBoolean(PreferenceUtils.Key.SCREEN_ZOOM_FIT)
forceRefreshRate = preferenceUtils.getBoolean(PreferenceUtils.Key.FORCE_HRR) forceRefreshRate = preferenceUtils.getBoolean(PreferenceUtils.Key.FORCE_HRR)
@@ -366,6 +366,22 @@ fun SettingsScreen(
) )
) )
} }
SettingsRangeCard(
title = stringResource(R.string.preference_screen_zoom_name),
dialogTitle = stringResource(R.string.preference_screen_zoom_select),
preferenceKey = PreferenceUtils.Key.SCREEN_ZOOM,
labelSuffix = "x",
range = 25..100,
scale = 100,
step = 5,
) {
SettingsCard(
title = context.getString(R.string.preference_screen_zoom_fit_name),
preferenceKey = PreferenceUtils.Key.SCREEN_ZOOM_FIT,
asCard = false
)
}
} }
OptionsGroup(context.getString(R.string.preference_category_updater)) { OptionsGroup(context.getString(R.string.preference_category_updater)) {
@@ -22,6 +22,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@@ -29,6 +30,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -48,6 +50,8 @@ import com.geode.launcher.ui.theme.GeodeLauncherTheme
import com.geode.launcher.ui.theme.Typography import com.geode.launcher.ui.theme.Typography
import com.geode.launcher.utils.LabelledText import com.geode.launcher.utils.LabelledText
import com.geode.launcher.utils.PreferenceUtils import com.geode.launcher.utils.PreferenceUtils
import kotlin.math.log10
import kotlin.math.roundToInt
fun toggleSetting(context: Context, preferenceKey: PreferenceUtils.Key): Boolean { fun toggleSetting(context: Context, preferenceKey: PreferenceUtils.Key): Boolean {
@@ -260,6 +264,113 @@ fun StringDialog(
) )
} }
@Composable
fun RangeDialog(
title: String,
onDismissRequest: () -> Unit,
onSelect: (Int) -> Unit,
labelSuffix: String,
initialValue: Int,
range: IntRange,
scale: Int,
step: Int,
children: @Composable () -> Unit
) {
var enteredValue by remember {
mutableFloatStateOf(initialValue / scale.toFloat())
}
AlertDialog(
onDismissRequest = { onDismissRequest() },
title = {
Text(title)
},
text = {
Column {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.weight(1.0f, false)
) {
val precision = log10(scale.toFloat()).roundToInt()
Slider(
value = enteredValue,
onValueChange = { enteredValue = it },
valueRange = (range.first.toFloat()/scale)..(range.last.toFloat()/scale),
steps = ((range.last - range.first) - 1) / step,
modifier = Modifier.weight(1.0f)
)
Text("%.${precision}f$labelSuffix".format(enteredValue))
}
children()
}
},
confirmButton = {
TextButton(onClick = { onSelect((enteredValue * scale).roundToInt()) }) {
Text(stringResource(R.string.message_box_accept))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(stringResource(R.string.message_box_cancel))
}
},
)
}
@Composable
fun SettingsRangeCard(
title: String,
dialogTitle: String,
preferenceKey: PreferenceUtils.Key,
labelSuffix: String,
range: IntRange,
scale: Int,
step: Int,
children: @Composable () -> Unit = {}
) {
var preferenceValue by PreferenceUtils.useIntPreference(preferenceKey)
var showDialog by remember { mutableStateOf(false) }
OptionsCard(
title = { OptionsTitle(title = title) },
modifier = Modifier
.clickable(
onClick = {
showDialog = true
},
role = Role.Button
)
) {
val precision = log10(scale.toFloat()).roundToInt()
val scaledValue = preferenceValue / scale.toFloat()
Text("%.${precision}f$labelSuffix".format(scaledValue))
}
if (showDialog) {
RangeDialog(
title = dialogTitle,
onDismissRequest = { showDialog = false },
onSelect = {
preferenceValue = it
showDialog = false
},
initialValue = preferenceValue,
range = range,
scale = scale,
labelSuffix = labelSuffix,
step = step,
children = children,
)
}
}
internal val LocalSelectValue = compositionLocalOf<Any> { 0 } internal val LocalSelectValue = compositionLocalOf<Any> { 0 }
internal val LocalSelectSetValue = staticCompositionLocalOf<(Any) -> Unit> { {} } internal val LocalSelectSetValue = staticCompositionLocalOf<(Any) -> Unit> { {} }
@@ -367,7 +478,7 @@ fun OptionsButton(title: String, description: String? = null, icon: (@Composable
} }
@Composable @Composable
fun SettingsCard(title: String, description: String? = null, icon: (@Composable () -> Unit)? = null, preferenceKey: PreferenceUtils.Key) { fun SettingsCard(title: String, description: String? = null, icon: (@Composable () -> Unit)? = null, asCard: Boolean = true, preferenceKey: PreferenceUtils.Key) {
val context = LocalContext.current val context = LocalContext.current
val settingEnabled = remember { val settingEnabled = remember {
mutableStateOf(getSetting(context, preferenceKey)) mutableStateOf(getSetting(context, preferenceKey))
@@ -382,13 +493,18 @@ fun SettingsCard(title: String, description: String? = null, icon: (@Composable
icon = icon icon = icon
) )
}, },
modifier = Modifier.toggleable( modifier = if (asCard) Modifier.toggleable(
value = settingEnabled.value, value = settingEnabled.value,
onValueChange = { settingEnabled.value = toggleSetting(context, preferenceKey) }, onValueChange = { settingEnabled.value = toggleSetting(context, preferenceKey) },
role = Role.Switch, role = Role.Switch,
) ) else Modifier
) { ) {
Switch(checked = settingEnabled.value, onCheckedChange = null) Switch(
checked = settingEnabled.value,
onCheckedChange = if (!asCard)
{ _ -> settingEnabled.value = toggleSetting(context, preferenceKey) }
else null
)
} }
} }
@@ -2,26 +2,52 @@ package com.geode.launcher.utils
import android.content.Context import android.content.Context
import android.widget.FrameLayout import android.widget.FrameLayout
import kotlin.math.roundToInt
class ConstrainedFrameLayout(context: Context) : FrameLayout(context) { class ConstrainedFrameLayout(context: Context) : FrameLayout(context) {
// avoid running the aspect ratio fix when we don't need it // avoid running the aspect ratio fix when we don't need it
var aspectRatio: Float? = null; var aspectRatio: Float? = null
var zoom: Float? = null
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
if (aspectRatio != null) { var paddingX = 0
var paddingY = 0
val currentAspectRatio = aspectRatio
if (currentAspectRatio != null) {
val width = MeasureSpec.getSize(widthMeasureSpec) val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec) val height = MeasureSpec.getSize(heightMeasureSpec)
if (width == 0 || height == 0) { if (width == 0 || height == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec) super.onMeasure(widthMeasureSpec, heightMeasureSpec)
return
} }
val calculatedWidth = height * (aspectRatio ?: 1.0f); val calculatedWidth = height * currentAspectRatio
val padding = ((width - calculatedWidth) / 2).toInt() val padding = ((width - calculatedWidth) / 2).toInt()
// ignore paddings where the screen ends up looking odd // ignore paddings where the screen ends up looking odd
if (padding > 0) { paddingX += padding
setPadding(padding, 0, padding, 0) }
val currentZoom = zoom
if (currentZoom != null) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
if (width == 0 || height == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
return
} }
val zoomFactor = 1 - currentZoom
val widthFactor = width/2.0f + paddingX/4.0f
val heightFactor = height/2.0f
paddingX = (widthFactor * zoomFactor).roundToInt()
paddingY = (heightFactor * zoomFactor).roundToInt()
}
if (paddingX > 0 || paddingY > 0) {
setPadding(paddingX, paddingY, paddingX, paddingY)
} }
super.onMeasure( super.onMeasure(
@@ -144,6 +144,8 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
CLEANUP_APKS, CLEANUP_APKS,
CUSTOM_SYMBOL_LIST, CUSTOM_SYMBOL_LIST,
CUSTOM_ASPECT_RATIO, CUSTOM_ASPECT_RATIO,
SCREEN_ZOOM,
SCREEN_ZOOM_FIT,
} }
private fun defaultValueForBooleanKey(key: Key): Boolean { private fun defaultValueForBooleanKey(key: Key): Boolean {
@@ -156,6 +158,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
private fun defaultValueForIntKey(key: Key) = when (key) { private fun defaultValueForIntKey(key: Key) = when (key) {
Key.DISPLAY_MODE -> if (this.getBoolean(Key.LIMIT_ASPECT_RATIO)) 1 else 0 Key.DISPLAY_MODE -> if (this.getBoolean(Key.LIMIT_ASPECT_RATIO)) 1 else 0
Key.SCREEN_ZOOM -> 10
// people wanted a reset on nightly anyways, so this is a good excuse to do so // people wanted a reset on nightly anyways, so this is a good excuse to do so
else -> 0 else -> 0
} }
@@ -187,6 +190,8 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
Key.CLEANUP_APKS -> "PreferenceCleanupPackages" Key.CLEANUP_APKS -> "PreferenceCleanupPackages"
Key.CUSTOM_SYMBOL_LIST -> "PreferenceCustomSymbolList" Key.CUSTOM_SYMBOL_LIST -> "PreferenceCustomSymbolList"
Key.CUSTOM_ASPECT_RATIO -> "PreferenceCustomAspectRatio" Key.CUSTOM_ASPECT_RATIO -> "PreferenceCustomAspectRatio"
Key.SCREEN_ZOOM -> "PreferenceScreenZoom"
Key.SCREEN_ZOOM_FIT -> "PreferenceScreenZoomFit"
} }
} }
+7 -3
View File
@@ -134,9 +134,9 @@
<string name="preference_force_hrr">Force maximum refresh rate</string> <string name="preference_force_hrr">Force maximum refresh rate</string>
<string name="preference_enable_redesign">✨ Enable UI refresh</string> <string name="preference_enable_redesign">✨ Enable UI refresh</string>
<string name="preference_display_mode_name">Screen scale</string> <string name="preference_display_mode_name">Display mode</string>
<string name="preference_display_mode_select">Select screen scale</string> <string name="preference_display_mode_select">Select display mode</string>
<string name="preference_display_mode_legacy">Limited (16:9)</string> <string name="preference_display_mode_legacy">Limited (Custom)</string>
<string name="preference_display_mode_fullscreen">Edge-to-edge</string> <string name="preference_display_mode_fullscreen">Edge-to-edge</string>
<string name="preference_display_mode_default">Default</string> <string name="preference_display_mode_default">Default</string>
@@ -147,6 +147,10 @@
<string name="preference_custom_aspect_ratio_4x3">4:3</string> <string name="preference_custom_aspect_ratio_4x3">4:3</string>
<string name="preference_custom_aspect_ratio_1x1">1:1</string> <string name="preference_custom_aspect_ratio_1x1">1:1</string>
<string name="preference_screen_zoom_name">Screen zoom</string>
<string name="preference_screen_zoom_select">Edit screen zoom</string>
<string name="preference_screen_zoom_fit_name">Fit to screen</string>
<string name="preference_category_updater">Updates</string> <string name="preference_category_updater">Updates</string>
<string name="preference_update_automatically_name">Enable automatic updates</string> <string name="preference_update_automatically_name">Enable automatic updates</string>
<string name="preference_release_channel_name">Use nightly release channel</string> <string name="preference_release_channel_name">Use nightly release channel</string>