diff --git a/app/src/main/java/app/jerboa/spp/MainActivity.kt b/app/src/main/java/app/jerboa/spp/MainActivity.kt index 11e3901..aae76a0 100644 --- a/app/src/main/java/app/jerboa/spp/MainActivity.kt +++ b/app/src/main/java/app/jerboa/spp/MainActivity.kt @@ -23,7 +23,7 @@ import app.jerboa.spp.viewmodel.SOCIAL import app.jerboa.spp.composable.screen import app.jerboa.spp.ui.theme.SPPTheme import app.jerboa.spp.viewmodel.AboutViewModel -import app.jerboa.spp.viewmodel.MainMenuViewModel +import app.jerboa.spp.viewmodel.ToyMenuViewModel import app.jerboa.spp.viewmodel.MenuPromptViewModel import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability @@ -90,7 +90,7 @@ class MainActivity : AppCompatActivity() { private val sppViewModel by viewModels() private val aboutViewModel by viewModels() private val menuPromptViewModel by viewModels() - private val mainMenuViewModel by viewModels() + private val toyMenuViewModel by viewModels() private var mediaPlayer = MediaPlayer() @@ -131,7 +131,9 @@ class MainActivity : AppCompatActivity() { "news" to R.drawable.news, "pause" to R.drawable.pause, "play" to R.drawable.play_button, - "freezer" to R.drawable.freezer + "freezer" to R.drawable.freezer, + "clear" to R.drawable.clear, + "toyMenu" to R.drawable.toymenu ) private val rcAchievementUI = 9003 @@ -444,7 +446,7 @@ class MainActivity : AppCompatActivity() { } ) - mainMenuViewModel.showToys.observe( + toyMenuViewModel.showToys.observe( this, androidx.lifecycle.Observer { show -> val prefs = getSharedPreferences("jerboa.app.spp.prefs", MODE_PRIVATE) val prefsEdit = prefs.edit() @@ -542,7 +544,7 @@ class MainActivity : AppCompatActivity() { prefsEdit.apply() } - mainMenuViewModel.onShowToysChanged(prefs.getBoolean("showToys", false)) + toyMenuViewModel.onShowToysChanged(prefs.getBoolean("showToys", false)) // if (BuildConfig.DEBUG){ // prefs.edit().clear().apply() @@ -600,7 +602,7 @@ class MainActivity : AppCompatActivity() { sppViewModel, aboutViewModel, menuPromptViewModel, - mainMenuViewModel, + toyMenuViewModel, Pair(width, height), imageResources, appInfo, diff --git a/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt b/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt index 11e38ba..93c0930 100644 --- a/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt +++ b/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt @@ -1,5 +1,6 @@ package app.jerboa.spp.composable +import android.util.Log import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.LinearEasing @@ -9,7 +10,6 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.Image import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -20,7 +20,6 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import app.jerboa.spp.viewmodel.AboutViewModel -import app.jerboa.spp.viewmodel.MUSIC import app.jerboa.spp.viewmodel.MenuPromptViewModel @OptIn(ExperimentalAnimationApi::class) @@ -36,246 +35,152 @@ fun menuPrompt( val displayingSound: Boolean by menuPromptViewModel.displayingSound.observeAsState(initial = false) val paused: Boolean by menuPromptViewModel.paused.observeAsState(initial = false) - val fadeAlpha = 0.33f - - val alphaM1: Float by animateFloatAsState( - targetValue = if (!displayingMenu) fadeAlpha else 1.0f, - animationSpec = tween( - durationMillis = 500, - easing = LinearEasing, - ), label = "m1" - ) - - val alphaM2: Float by animateFloatAsState( - targetValue = if (displayingMenu) fadeAlpha else 1.0f, + val fadePromptAlpha: Float by animateFloatAsState( + targetValue = if (!displayingMenu) 0.33f else 1.0f, animationSpec = tween( durationMillis = 500, easing = LinearEasing, - ), label = "m2" + ), label = "alpha for fading the prompt" ) - val alphaS1: Float by animateFloatAsState( - targetValue = if (!displayingSound) fadeAlpha else 1.0f, - animationSpec = tween( - durationMillis = 500, - easing = LinearEasing, - ), label = "s1" - ) - - val alphaS2: Float by animateFloatAsState( - targetValue = if (displayingSound) fadeAlpha else 1.0f, - animationSpec = tween( - durationMillis = 500, - easing = LinearEasing, - ), label = "s2" - ) - - Box(modifier = Modifier .fillMaxHeight() - .fillMaxWidth()) { + .fillMaxWidth() + .padding(16.dp)) { Box( modifier = Modifier - .width(menuItemHeight.dp) - .height((menuItemHeight * 2.0).dp) - .padding((menuItemHeight * 0.1).dp) + .fillMaxHeight() + .fillMaxWidth() .align(alignment = Alignment.BottomStart) ) { - AnimatedVisibility( - visible = !displayingMenu, - enter = fadeIn(), - exit = fadeOut() - ) { - Image( - painter = painterResource(id = images["burger"]!!), - contentDescription = "menu", - modifier = Modifier - .fillMaxSize() - .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { menuPromptViewModel.onDisplayingMenuChanged(true); } - ) - .alpha(alphaM1) - ) - } AnimatedVisibility( visible = displayingMenu, - enter = fadeIn(), - exit = fadeOut() + enter = fadeIn(tween(0)), + exit = fadeOut(tween(0)) ) { - Image( - painter = painterResource(id = images["dismiss"]!!), - contentDescription = "menu", - modifier = Modifier - .fillMaxSize() - .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { - menuPromptViewModel.onDisplayingMenuChanged(false) - aboutViewModel.onDisplayingAboutChanged(false) - } - ) - .alpha(alphaM2) - ) - } - } - Box( - modifier = Modifier - .width(menuItemHeight.dp) - .height((menuItemHeight * 8.0).dp) - .padding((menuItemHeight * 0.1).dp) - .align(alignment = Alignment.BottomEnd) - ) { - AnimatedVisibility( - visible = displayingSound, - enter = fadeIn(), - exit = fadeOut() - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.spacedBy((0.01*menuItemHeight).dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { + verticalMenu(modifier = Modifier, offset = Pair(0.dp, menuItemHeight.dp), contentPadding = 16.dp) { Image( - painter = painterResource(id = images["music-rain"]!!), - contentDescription = "rain", + painter = painterResource(id = images["toyMenu"]!!), + contentDescription = "Toy menu and sliders", modifier = Modifier - .fillMaxWidth().height((menuItemHeight*2.0).dp) + .size(menuItemHeight.dp) .clickable( - interactionSource = MutableInteractionSource(), - indication = null, onClick = { + menuPromptViewModel.onDisplayingToyMenuChanged(true) menuPromptViewModel.onDisplayingMusicChanged(false) aboutViewModel.onDisplayingAboutChanged(false) - menuPromptViewModel.onMusicSelected(MUSIC.RAIN) } ) - .alpha(alphaS2) - ) - Image( - painter = painterResource(id = images["music-forrest"]!!), - contentDescription = "forrest", - modifier = Modifier - .fillMaxWidth().height((menuItemHeight*2.0).dp) - .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { - menuPromptViewModel.onDisplayingMusicChanged(false) - aboutViewModel.onDisplayingAboutChanged(false) - menuPromptViewModel.onMusicSelected(MUSIC.FORREST) - } - ) - .alpha(alphaS2) ) + if (displayingSound) { + musicMenu( + menuPromptViewModel, + menuItemHeight, + images + ) + } + else { + Image( + painter = painterResource(id = images["music"]!!), + contentDescription = "Music menu", + modifier = Modifier + .size(menuItemHeight.dp) + .clickable( + onClick = { + menuPromptViewModel.onDisplayingToyMenuChanged(false) + menuPromptViewModel.onDisplayingMusicChanged(true) + aboutViewModel.onDisplayingAboutChanged(false) + } + ) + ) + } Image( - painter = painterResource(id = images["music-none"]!!), - contentDescription = "no sound", + painter = painterResource(id = images["clear"]!!), + contentDescription = "clear all the toys", modifier = Modifier - .fillMaxWidth().height((menuItemHeight*2.0).dp) + .size(menuItemHeight.dp) .clickable( - interactionSource = MutableInteractionSource(), - indication = null, onClick = { - menuPromptViewModel.onDisplayingMusicChanged(false) - aboutViewModel.onDisplayingAboutChanged(false) - menuPromptViewModel.onMusicSelected(MUSIC.NOTHING) + menuPromptViewModel.onClear(true) } ) - .alpha(alphaS2) ) + if (!paused) { + Image( + painter = painterResource(id = images["pause"]!!), + contentDescription = "pause the particles", + modifier = Modifier + .size(menuItemHeight.dp) + .clickable( + onClick = { menuPromptViewModel.onPause(); } + ) + ) + } else { + Image( + painter = painterResource(id = images["play"]!!), + contentDescription = "unpause the particles", + modifier = Modifier + .size(menuItemHeight.dp) + .clickable( + onClick = { menuPromptViewModel.onPause(); } + ) + ) + } Image( - painter = painterResource(id = images["dismiss"]!!), - contentDescription = "menu", + painter = painterResource(id = images["about"]!!), + contentDescription = "About and news", modifier = Modifier - .fillMaxWidth().height((menuItemHeight*2.0).dp) + .size(menuItemHeight.dp) .clickable( - interactionSource = MutableInteractionSource(), - indication = null, onClick = { + menuPromptViewModel.onDisplayingToyMenuChanged(false) menuPromptViewModel.onDisplayingMusicChanged(false) - aboutViewModel.onDisplayingAboutChanged(false) + aboutViewModel.onDisplayingAboutChanged(true) } ) - .alpha(alphaS2) ) } } } Box( modifier = Modifier - .width(menuItemHeight.dp) - .height((menuItemHeight * 2.0).dp) - .padding((menuItemHeight * 0.1).dp) - .align(alignment = Alignment.BottomCenter) + .size(menuItemHeight.dp) + .align(alignment = Alignment.BottomStart) ) { AnimatedVisibility( - visible = !paused && !displayingMenu, - enter = fadeIn(), - exit = fadeOut() - ) { - Image( - painter = painterResource(id = images["pause"]!!), - contentDescription = "menu", - modifier = Modifier - .fillMaxSize() - .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { menuPromptViewModel.onPause(); } - ) - .alpha(0.66f) - ) - } - AnimatedVisibility( - visible = paused && !displayingMenu, + visible = !displayingMenu, enter = fadeIn(), exit = fadeOut() ) { Image( - painter = painterResource(id = images["play"]!!), - contentDescription = "menu", + painter = painterResource(id = images["burger"]!!), + contentDescription = "Open the menu", modifier = Modifier - .fillMaxSize() + .size(menuItemHeight.dp) .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { menuPromptViewModel.onPause(); } + onClick = { menuPromptViewModel.onDisplayingMenuChanged(true); } ) - .alpha(0.66f) + .alpha(fadePromptAlpha) ) } - } - Box( - modifier = Modifier - .width(menuItemHeight.dp) - .height((menuItemHeight * 2.0).dp) - .padding((menuItemHeight * 0.1).dp) - .align(alignment = Alignment.BottomEnd) - ) { AnimatedVisibility( - visible = !displayingSound, + visible = displayingMenu, enter = fadeIn(), exit = fadeOut() ) { Image( - painter = painterResource(id = images["music"]!!), - contentDescription = "menu", + painter = painterResource(id = images["dismiss"]!!), + contentDescription = "Close the menu", modifier = Modifier - .fillMaxSize() + .size(menuItemHeight.dp) .clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = - { - menuPromptViewModel.onDisplayingMusicChanged(true) + onClick = { + menuPromptViewModel.onDisplayingMenuChanged(false) + aboutViewModel.onDisplayingAboutChanged(false) + menuPromptViewModel.onDisplayingToyMenuChanged(false) + menuPromptViewModel.onDisplayingMusicChanged(false) } ) - .alpha(alphaS1) ) } } diff --git a/app/src/main/java/app/jerboa/spp/composable/musicMenu.kt b/app/src/main/java/app/jerboa/spp/composable/musicMenu.kt new file mode 100644 index 0000000..e8ac7bf --- /dev/null +++ b/app/src/main/java/app/jerboa/spp/composable/musicMenu.kt @@ -0,0 +1,85 @@ +package app.jerboa.spp.composable + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import app.jerboa.spp.viewmodel.AboutViewModel +import app.jerboa.spp.viewmodel.MUSIC +import app.jerboa.spp.viewmodel.MenuPromptViewModel + +@Composable +fun musicMenu( + menuPromptViewModel: MenuPromptViewModel, + menuItemHeight: Double, + images: Map, +) { + AnimatedVisibility( + visible = true, + enter = fadeIn(), + exit = fadeOut() + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(menuItemHeight.dp), + horizontalArrangement = Arrangement.spacedBy((0.01*menuItemHeight).dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = images["music-rain"]!!), + contentDescription = "rain", + modifier = Modifier + .size(menuItemHeight.dp) + .clickable( + onClick = { + menuPromptViewModel.onDisplayingMusicChanged(false) + menuPromptViewModel.onMusicSelected(MUSIC.RAIN) + } + ) + ) + Image( + painter = painterResource(id = images["music-forrest"]!!), + contentDescription = "forrest", + modifier = Modifier + .size(menuItemHeight.dp) + .clickable( + onClick = { + menuPromptViewModel.onDisplayingMusicChanged(false) + menuPromptViewModel.onMusicSelected(MUSIC.FORREST) + } + ) + ) + Image( + painter = painterResource(id = images["music-none"]!!), + contentDescription = "no sound", + modifier = Modifier + .size(menuItemHeight.dp) + .clickable( + onClick = { + menuPromptViewModel.onDisplayingMusicChanged(false) + menuPromptViewModel.onMusicSelected(MUSIC.NOTHING) + } + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/jerboa/spp/composable/screen.kt b/app/src/main/java/app/jerboa/spp/composable/screen.kt index 57a6abc..d8b6b6f 100644 --- a/app/src/main/java/app/jerboa/spp/composable/screen.kt +++ b/app/src/main/java/app/jerboa/spp/composable/screen.kt @@ -28,7 +28,7 @@ fun screen( sppViewModel: SPPViewModel, aboutViewModel: AboutViewModel, menuPromptViewModel: MenuPromptViewModel, - mainMenuViewModel: MainMenuViewModel, + toyMenuViewModel: ToyMenuViewModel, resolution: Pair, images: Map, info: AppInfo, @@ -43,23 +43,23 @@ fun screen( val height10Percent = info.heightDp * 0.1 val menuItemHeight = height10Percent * 0.66 - val displayingAbout: Boolean by aboutViewModel.displayingAbout.observeAsState(initial = false) - val displayingMenu: Boolean by menuPromptViewModel.displayingMenu.observeAsState(initial = false) + val displayingToyMenu: Boolean by menuPromptViewModel.displayingToyMenu.observeAsState(initial = false) val paused: Boolean by menuPromptViewModel.paused.observeAsState(initial = false) + val clear: Boolean by menuPromptViewModel.clear.observeAsState(initial = false) - val toy: TOY by mainMenuViewModel.toy.observeAsState(initial = TOY.ATTRACTOR) - val particleNumber: Float by mainMenuViewModel.particleNumber.observeAsState(initial = PARTICLES_SLIDER_DEFAULT) - val allowAdapt: Boolean by mainMenuViewModel.allowAdapt.observeAsState(initial = true) - val adaptMsg: Boolean by mainMenuViewModel.autoAdaptMessage.observeAsState(initial = false) - val colourMap: COLOUR_MAP by mainMenuViewModel.colourMap.observeAsState(COLOUR_MAP.R1) - val speed: Float by mainMenuViewModel.speed.observeAsState(initial = 1.0f) - val attraction: Float by mainMenuViewModel.attractorStrength.observeAsState(50000f) - val repulsion: Float by mainMenuViewModel.repellorStrength.observeAsState(50000f) - val orbit: Float by mainMenuViewModel.orbitStrength.observeAsState(0.5f) - val spin: Float by mainMenuViewModel.spinStrength.observeAsState(1500f) - val mass: Float by mainMenuViewModel.mass.observeAsState(0.1f) - val fade: Float by mainMenuViewModel.fade.observeAsState(initial = 1.0f) - val showToys: Boolean by mainMenuViewModel.showToys.observeAsState(initial = false) + val toy: TOY by toyMenuViewModel.toy.observeAsState(initial = TOY.ATTRACTOR) + val particleNumber: Float by toyMenuViewModel.particleNumber.observeAsState(initial = PARTICLES_SLIDER_DEFAULT) + val allowAdapt: Boolean by toyMenuViewModel.allowAdapt.observeAsState(initial = true) + val adaptMsg: Boolean by toyMenuViewModel.autoAdaptMessage.observeAsState(initial = false) + val colourMap: COLOUR_MAP by toyMenuViewModel.colourMap.observeAsState(COLOUR_MAP.R1) + val speed: Float by toyMenuViewModel.speed.observeAsState(initial = 1.0f) + val attraction: Float by toyMenuViewModel.attractorStrength.observeAsState(50000f) + val repulsion: Float by toyMenuViewModel.repellorStrength.observeAsState(50000f) + val orbit: Float by toyMenuViewModel.orbitStrength.observeAsState(0.5f) + val spin: Float by toyMenuViewModel.spinStrength.observeAsState(1500f) + val mass: Float by toyMenuViewModel.mass.observeAsState(0.1f) + val fade: Float by toyMenuViewModel.fade.observeAsState(initial = 1.0f) + val showToys: Boolean by toyMenuViewModel.showToys.observeAsState(initial = false) val promptPGS: Boolean by sppViewModel.promptInstallPGS.observeAsState(initial = false) @@ -67,7 +67,7 @@ fun screen( val scaffoldState = rememberScaffoldState() val coroutineScope = rememberCoroutineScope() - mainMenuViewModel.selectDefaultColourMap() + toyMenuViewModel.selectDefaultColourMap() Column( modifier = Modifier.fillMaxWidth() @@ -79,11 +79,11 @@ fun screen( topBar = { }, bottomBar = { - mainMenu( - mainMenuViewModel, + toyMenu( + toyMenuViewModel, sppViewModel, aboutViewModel, - displayingMenu && !displayingAbout, + displayingToyMenu, width75Percent, height10Percent, menuItemHeight, @@ -92,7 +92,7 @@ fun screen( } ) { if (adaptMsg) { - mainMenuViewModel.onAdaptMessageShown() + toyMenuViewModel.onAdaptMessageShown() coroutineScope.launch { val snackbarResult = scaffoldState.snackbarHostState.showSnackbar( message = "FPS lower than 30 adapting...", @@ -102,7 +102,7 @@ fun screen( when (snackbarResult) { //SnackbarResult.Dismissed -> Log.d("screen", "Dismissed") SnackbarResult.ActionPerformed -> { - mainMenuViewModel.onAllowAdaptChanged() + toyMenuViewModel.onAllowAdaptChanged() } else -> {} } @@ -131,7 +131,7 @@ fun screen( it, null, resolution, sppViewModel, - mainMenuViewModel, + toyMenuViewModel, toy, particleNumber, allowAdapt, @@ -153,6 +153,7 @@ fun screen( view.setSpin(spin) view.setFade(fade) view.showToys(showToys) + if (clear) { view.clearToys(); menuPromptViewModel.onClear(false); } } ) } diff --git a/app/src/main/java/app/jerboa/spp/composable/mainMenu.kt b/app/src/main/java/app/jerboa/spp/composable/toyMenu.kt similarity index 85% rename from app/src/main/java/app/jerboa/spp/composable/mainMenu.kt rename to app/src/main/java/app/jerboa/spp/composable/toyMenu.kt index ee27ee5..14f75bd 100644 --- a/app/src/main/java/app/jerboa/spp/composable/mainMenu.kt +++ b/app/src/main/java/app/jerboa/spp/composable/toyMenu.kt @@ -36,7 +36,7 @@ import app.jerboa.spp.viewmodel.MIN_LOG_FADE import app.jerboa.spp.viewmodel.MIN_LOG_MASS import app.jerboa.spp.viewmodel.MIN_LOG_ORBIT import app.jerboa.spp.viewmodel.MIN_LOG_SPIN -import app.jerboa.spp.viewmodel.MainMenuViewModel +import app.jerboa.spp.viewmodel.ToyMenuViewModel import app.jerboa.spp.viewmodel.PARAM import app.jerboa.spp.viewmodel.PARTICLES_SLIDER_DEFAULT import app.jerboa.spp.viewmodel.SPPViewModel @@ -102,8 +102,8 @@ fun logSlider( } @OptIn(ExperimentalAnimationApi::class) @Composable -fun mainMenu( - mainMenuViewModel: MainMenuViewModel, +fun toyMenu( + toyMenuViewModel: ToyMenuViewModel, sppViewModel: SPPViewModel, aboutViewModel: AboutViewModel, displayingMenu: Boolean, @@ -114,15 +114,15 @@ fun mainMenu( ) { val playSuccess: Boolean by sppViewModel.playSuccess.observeAsState(initial = false) - val particleNumber: Float by mainMenuViewModel.particleNumber.observeAsState(initial = PARTICLES_SLIDER_DEFAULT) - val speed: Float by mainMenuViewModel.speed.observeAsState(initial = 1.0f) - val attraction: Float by mainMenuViewModel.attractorStrength.observeAsState(50000f) - val repulsion: Float by mainMenuViewModel.repellorStrength.observeAsState(50000f) - val orbit: Float by mainMenuViewModel.orbitStrength.observeAsState(0.5f) - val spin: Float by mainMenuViewModel.spinStrength.observeAsState(1500f) - val mass: Float by mainMenuViewModel.mass.observeAsState(0.1f) - val fade: Float by mainMenuViewModel.fade.observeAsState(initial = 1.0f) - val showToys: Boolean by mainMenuViewModel.showToys.observeAsState(initial = false) + val particleNumber: Float by toyMenuViewModel.particleNumber.observeAsState(initial = PARTICLES_SLIDER_DEFAULT) + val speed: Float by toyMenuViewModel.speed.observeAsState(initial = 1.0f) + val attraction: Float by toyMenuViewModel.attractorStrength.observeAsState(50000f) + val repulsion: Float by toyMenuViewModel.repellorStrength.observeAsState(50000f) + val orbit: Float by toyMenuViewModel.orbitStrength.observeAsState(0.5f) + val spin: Float by toyMenuViewModel.spinStrength.observeAsState(1500f) + val mass: Float by toyMenuViewModel.mass.observeAsState(0.1f) + val fade: Float by toyMenuViewModel.fade.observeAsState(initial = 1.0f) + val showToys: Boolean by toyMenuViewModel.showToys.observeAsState(initial = false) var particleSliderValue by remember { mutableFloatStateOf(log10(particleNumber)) @@ -155,7 +155,7 @@ fun mainMenu( verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth(), ) { - colourMapMenu(images, menuItemHeight) { mainMenuViewModel.onSelectColourMap(it) } + colourMapMenu(images, menuItemHeight) { toyMenuViewModel.onSelectColourMap(it) } Box( modifier = Modifier @@ -218,7 +218,7 @@ fun mainMenu( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { - IconButton(onClick = { mainMenuViewModel.onToyChanged(TOY.ATTRACTOR) }) { + IconButton(onClick = { toyMenuViewModel.onToyChanged(TOY.ATTRACTOR) }) { Image( modifier = Modifier .fillMaxHeight() @@ -228,7 +228,7 @@ fun mainMenu( contentDescription = "Image" ) } - IconButton(onClick = { mainMenuViewModel.onToyChanged(TOY.REPELLOR) }) { + IconButton(onClick = { toyMenuViewModel.onToyChanged(TOY.REPELLOR) }) { Image( modifier = Modifier .fillMaxHeight() @@ -238,7 +238,7 @@ fun mainMenu( contentDescription = "Image" ) } - IconButton(onClick = { mainMenuViewModel.onToyChanged(TOY.SPINNER) }) { + IconButton(onClick = { toyMenuViewModel.onToyChanged(TOY.SPINNER) }) { Image( modifier = Modifier .fillMaxHeight() @@ -248,7 +248,7 @@ fun mainMenu( contentDescription = "Image" ) } - IconButton(onClick = { mainMenuViewModel.onToyChanged(TOY.FREEZER) }) { + IconButton(onClick = { toyMenuViewModel.onToyChanged(TOY.FREEZER) }) { Image( modifier = Modifier .fillMaxHeight() @@ -258,7 +258,7 @@ fun mainMenu( contentDescription = "Image" ) } - IconButton(onClick = { mainMenuViewModel.onToyChanged(TOY.ORBITER) }) { + IconButton(onClick = { toyMenuViewModel.onToyChanged(TOY.ORBITER) }) { Image( modifier = Modifier .fillMaxHeight() @@ -285,7 +285,7 @@ fun mainMenu( value = particleSliderValue, onValueChange = { particleSliderValue = it }, onValueChangeFinished = { - mainMenuViewModel.onParameterChanged( + toyMenuViewModel.onParameterChanged( Pair( 10.0f.pow( particleSliderValue @@ -301,14 +301,14 @@ fun mainMenu( .background(color = Color(1, 1, 1, 1)) ) Spacer(modifier = Modifier.size(8.dp)) - logSlider(speed, -3.0f, MAX_LOG_SPEED, "Speed", {mainMenuViewModel.onParameterChanged(Pair(it, PARAM.SPEED))}, width75Percent) + logSlider(speed, -3.0f, MAX_LOG_SPEED, "Speed", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SPEED))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) logSlider( attraction, MIN_LOG_AR, MAX_LOG_AR, "Attraction", - {mainMenuViewModel.onParameterChanged(Pair(it, PARAM.ATTRACTION))}, + {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.ATTRACTION))}, width75Percent ) Spacer(modifier = Modifier.size(8.dp)) @@ -317,22 +317,22 @@ fun mainMenu( MIN_LOG_AR, MAX_LOG_AR, "Repulsion", - {mainMenuViewModel.onParameterChanged(Pair(it, PARAM.REPULSION))}, + {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.REPULSION))}, width75Percent ) Spacer(modifier = Modifier.size(8.dp)) - logSlider(orbit, MIN_LOG_ORBIT, MAX_LOG_ORBIT, "Orbit", {mainMenuViewModel.onParameterChanged(Pair(it, PARAM.ORBIT))}, width75Percent) + logSlider(orbit, MIN_LOG_ORBIT, MAX_LOG_ORBIT, "Orbit", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.ORBIT))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) - logSlider(spin, MIN_LOG_SPIN, MAX_LOG_SPIN, "Spin", {mainMenuViewModel.onParameterChanged(Pair(it, PARAM.SPIN))}, width75Percent) + logSlider(spin, MIN_LOG_SPIN, MAX_LOG_SPIN, "Spin", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SPIN))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) - logSlider(mass, MIN_LOG_MASS, MAX_LOG_MASS, "Mass", {mainMenuViewModel.onParameterChanged(Pair(it, PARAM.MASS))}, width75Percent) + logSlider(mass, MIN_LOG_MASS, MAX_LOG_MASS, "Mass", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.MASS))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) label(text = "Tracing " + "${round(fadeSliderValue * 100.0f) / 100.0f}") Slider( value = fadeSliderValue, onValueChange = { fadeSliderValue = it }, onValueChangeFinished = { - mainMenuViewModel.onParameterChanged( + toyMenuViewModel.onParameterChanged( Pair( 10.0f.pow((1.0f - fadeSliderValue) * (MAX_LOG_FADE - MIN_LOG_FADE) + MIN_LOG_FADE), PARAM.FADE @@ -349,7 +349,7 @@ fun mainMenu( label(text = "Show Toys") Checkbox( checked = showToysValue, - onCheckedChange = { showToysValue = it; mainMenuViewModel.onShowToysChanged(it) }, + onCheckedChange = { showToysValue = it; toyMenuViewModel.onShowToysChanged(it) }, colors = checkBoxColors() ) } diff --git a/app/src/main/java/app/jerboa/spp/composable/verticalMenu.kt b/app/src/main/java/app/jerboa/spp/composable/verticalMenu.kt new file mode 100644 index 0000000..bd44087 --- /dev/null +++ b/app/src/main/java/app/jerboa/spp/composable/verticalMenu.kt @@ -0,0 +1,38 @@ +package app.jerboa.spp.composable + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import kotlin.math.max + +@Composable +fun verticalMenu( + modifier: Modifier, + contentPadding: Dp = 0.dp, + offset: Pair = Pair(0.dp, 0.dp), + content: @Composable () -> Unit +) { + val configuration = LocalConfiguration.current + + Layout(content = content, modifier = modifier) { children, constraints -> + val screenWidth = configuration.screenWidthDp.dp.toPx() + val screenHeight = configuration.screenHeightDp.dp.toPx() + val pad = contentPadding.toPx() + val offsetPx = Pair(offset.first.toPx().toInt(), offset.second.toPx().toInt()) + val items = children.map { it.measure(constraints) } + + var maxHeight = 0 + items.map { maxHeight = max(maxHeight, it.height) } + + layout(screenWidth.toInt(), screenWidth.toInt()) { + var y = 0 + for (i in items.indices) { + items[i].placeRelative(x=pad.toInt()+offsetPx.first, y=(screenHeight-y-maxHeight-offsetPx.second-pad.toInt()).toInt()) + y += items[i].height + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt b/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt index 2eea551..a877377 100644 --- a/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt +++ b/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt @@ -35,7 +35,7 @@ enum class DRAG {START, STOP, CONTINUE} class SPPRenderer( private val resolution: Pair, private val sppViewModel: SPPViewModel, - private val mainMenuViewModel: MainMenuViewModel, + private val toyMenuViewModel: ToyMenuViewModel, private var particleNumber: Float = PARTICLES_SLIDER_DEFAULT, private var allowAdapt: Boolean = true, private var colourMap: COLOUR_MAP = COLOUR_MAP.R1 @@ -302,6 +302,14 @@ class SPPRenderer( fun setOrbit(v: Float) { orbitStrength = v } fun setShowToys(v: Boolean){ showToys = v } + fun clearToys() { + attractors.clear() + repellors.clear() + spinners.clear() + orbiters.clear() + freezers.clear() + } + fun setParticleNumber(v: Float){ if (particleNumber == v){return} particleNumber = v @@ -398,7 +406,7 @@ class SPPRenderer( val toy = toySelected(wx, wy) if (toy != null) { touchEvent.draggedToy = toy - mainMenuViewModel.onToyChanged(toy.first) + toyMenuViewModel.onToyChanged(toy.first) touchEvent.dragPlacedToy = false } else { when (toyType) { @@ -407,7 +415,7 @@ class SPPRenderer( attractors.add(Pair(wx, wy)) } touchEvent.draggedToy = Pair(TOY.ATTRACTOR, attractors.sizeBack - 1) - mainMenuViewModel.onToyChanged(TOY.ATTRACTOR) + toyMenuViewModel.onToyChanged(TOY.ATTRACTOR) touchEvent.dragPlacedToy = true } @@ -416,7 +424,7 @@ class SPPRenderer( repellors.add(Pair(wx, wy)) } touchEvent.draggedToy = Pair(TOY.REPELLOR, repellors.sizeBack - 1) - mainMenuViewModel.onToyChanged(TOY.REPELLOR) + toyMenuViewModel.onToyChanged(TOY.REPELLOR) touchEvent.dragPlacedToy = true } @@ -425,7 +433,7 @@ class SPPRenderer( spinners.add(Pair(wx, wy)) } touchEvent.draggedToy = Pair(TOY.SPINNER, spinners.sizeBack - 1) - mainMenuViewModel.onToyChanged(TOY.SPINNER) + toyMenuViewModel.onToyChanged(TOY.SPINNER) touchEvent.dragPlacedToy = true } @@ -434,7 +442,7 @@ class SPPRenderer( freezers.add(Pair(wx, wy)) } touchEvent.draggedToy = Pair(TOY.FREEZER, freezers.sizeBack - 1) - mainMenuViewModel.onToyChanged(TOY.FREEZER) + toyMenuViewModel.onToyChanged(TOY.FREEZER) touchEvent.dragPlacedToy = true } @@ -443,7 +451,7 @@ class SPPRenderer( orbiters.add(Pair(wx, wy)) } touchEvent.draggedToy = Pair(TOY.ORBITER, orbiters.sizeBack - 1) - mainMenuViewModel.onToyChanged(TOY.ORBITER) + toyMenuViewModel.onToyChanged(TOY.ORBITER) touchEvent.dragPlacedToy = true } @@ -634,7 +642,7 @@ class SPPRenderer( newP = p reset = true - mainMenuViewModel.onAdapt(particleNumber) + toyMenuViewModel.onAdapt(particleNumber) } private fun compileDrawShader(){ diff --git a/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt b/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt index c347b43..d25c96b 100644 --- a/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt +++ b/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt @@ -3,12 +3,10 @@ package app.jerboa.spp.ui.view import android.content.Context import android.opengl.GLSurfaceView import android.util.AttributeSet -import android.util.Log import android.view.GestureDetector import android.view.MotionEvent -import androidx.core.view.GestureDetectorCompat import app.jerboa.spp.viewmodel.COLOUR_MAP -import app.jerboa.spp.viewmodel.MainMenuViewModel +import app.jerboa.spp.viewmodel.ToyMenuViewModel import app.jerboa.spp.viewmodel.PARTICLES_SLIDER_DEFAULT import app.jerboa.spp.viewmodel.SPPViewModel import app.jerboa.spp.viewmodel.TOY @@ -18,7 +16,7 @@ class SPPView ( attr: AttributeSet? = null, private val resolution: Pair, private val sppViewModel: SPPViewModel, - private val mainMenuViewModel: MainMenuViewModel, + private val toyMenuViewModel: ToyMenuViewModel, var placingToy: TOY = TOY.ATTRACTOR, var particleNumber: Float = PARTICLES_SLIDER_DEFAULT, private var allowAdapt: Boolean = true, @@ -28,7 +26,7 @@ class SPPView ( private val renderer = SPPRenderer( resolution, sppViewModel, - mainMenuViewModel, + toyMenuViewModel, particleNumber, allowAdapt, colourMap @@ -66,6 +64,7 @@ class SPPView ( fun setSpin(v: Float) { renderer.setSpin(v) } fun setOrbit(v: Float) { renderer.setOrbit(v) } fun showToys(v: Boolean){ renderer.setShowToys(v) } + fun clearToys() { renderer.clearToys() } override fun onResume() { super.onResume() diff --git a/app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt b/app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt index 9282d11..7248c54 100644 --- a/app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt +++ b/app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt @@ -34,7 +34,7 @@ enum class TOY {ATTRACTOR,REPELLOR,SPINNER,FREEZER,ORBITER, NOTHING} enum class PARAM {MASS, SPEED, ATTRACTION, REPULSION, ORBIT, SPIN, PARTICLES, FADE} -class MainMenuViewModel: ViewModel() { +class ToyMenuViewModel: ViewModel() { private val _colourMap = MutableLiveData(COLOUR_MAP.R1) val colourMap: MutableLiveData = _colourMap diff --git a/app/src/main/java/app/jerboa/spp/viewmodel/menuPromptViewModel.kt b/app/src/main/java/app/jerboa/spp/viewmodel/menuPromptViewModel.kt index 5bc668d..8050fe2 100644 --- a/app/src/main/java/app/jerboa/spp/viewmodel/menuPromptViewModel.kt +++ b/app/src/main/java/app/jerboa/spp/viewmodel/menuPromptViewModel.kt @@ -10,40 +10,42 @@ class MenuPromptViewModel : ViewModel() { private val _displayingMenu = MutableLiveData(false) val displayingMenu : MutableLiveData = _displayingMenu - //private val _lastClick = MutableLiveData(0L) - fun onDisplayingMenuChanged(newVal: Boolean){ + fun onDisplayingMenuChanged(newVal: Boolean) { _displayingMenu.value = newVal + } - if (_displayingMenu.value == true){ - _displayingSound.value = false - } - - //_lastClick.value = System.currentTimeMillis() + private val _displayingToyMenu = MutableLiveData(false) + val displayingToyMenu: MutableLiveData = _displayingToyMenu + fun onDisplayingToyMenuChanged(newVal: Boolean) { + _displayingToyMenu.value = newVal } private val _displayingSound = MutableLiveData(false) val displayingSound: MutableLiveData = _displayingSound - fun onDisplayingMusicChanged(newVal: Boolean){ + fun onDisplayingMusicChanged(newVal: Boolean) { _displayingSound.value = newVal - if (_displayingSound.value == true){ - _displayingMenu.value = false - } } private val _playingMusic = MutableLiveData(MUSIC.NOTHING) val playingMusic: MutableLiveData = _playingMusic - fun onMusicSelected(v: MUSIC){ + fun onMusicSelected(v: MUSIC) { _playingMusic.value = v } private val _paused = MutableLiveData(false) val paused: MutableLiveData = _paused - fun onPause() - { + fun onPause() { _paused.postValue(!_paused.value!!) } + private val _clear = MutableLiveData(false) + val clear: MutableLiveData = _clear + + fun onClear(v: Boolean) { + _clear.postValue(v) + } + } \ No newline at end of file diff --git a/app/src/main/res/drawable/clear.xml b/app/src/main/res/drawable/clear.xml new file mode 100644 index 0000000..cbcdcd6 --- /dev/null +++ b/app/src/main/res/drawable/clear.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/toymenu.xml b/app/src/main/res/drawable/toymenu.xml new file mode 100644 index 0000000..ded90db --- /dev/null +++ b/app/src/main/res/drawable/toymenu.xml @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d5e6ef0..8d0c15f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ 17196155283 app.jerboa.spp - "Place \"attractors\", \"repellers\", \"spinners\", and \"freezers\" to manipulate the particles.\n\n License: GNU General Public License v3.0" + "Place \"attractors\", \"repellers\", \"spinners\", \"orbiters\", and \"freezers\" to manipulate the particles. Change their strengths in the menu.\n\n License: GNU General Public License v3.0" "Click for open Source Software we depend on! (Licenses)" Some colour maps from/modified from ColorCET, CC-BY. Some Sounds mixed from freesound.org inc. CC-BY: "Rain on Windows, Interior, B.wav" by InspectorJ (www.jshaw.co.uk) and CC0: FlatHill, bsyapril88, suoguolh, deleted_user_229898, jackthemurray, Lydmakeren, and Sandermotions. "If you had fun, why not rate us on the Play Store? Thanks!" diff --git a/assets/icons/clear.svg b/assets/icons/clear.svg new file mode 100755 index 0000000..c706107 --- /dev/null +++ b/assets/icons/clear.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/assets/icons/toyMenu.svg b/assets/icons/toyMenu.svg new file mode 100644 index 0000000..ac2267b --- /dev/null +++ b/assets/icons/toyMenu.svg @@ -0,0 +1,1141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +