Change the blur mechanism

This commit is contained in:
Thomas Andres Gomez 2024-11-12 16:57:54 +01:00
parent 3705fbd947
commit 5ac0c2dcf6
7 changed files with 172 additions and 73 deletions

View file

@ -0,0 +1,74 @@
package com.pixelized.desktop.lwa.composable.blur
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.BlurredEdgeTreatment
import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.theme.LwaColorPalette
@Stable
class BlurContentController(
blurred: Boolean = false,
val blurredRadius: Dp = 8.dp,
val scrimColor: Color = LwaColorPalette.DefaultScrimColor,
) {
private val _blurred = mutableStateOf(blurred)
val isBlurred: State<Boolean> get() = _blurred
fun show() {
_blurred.value = true
}
fun hide() {
_blurred.value = false
}
}
@Composable
fun BlurContent(
modifier: Modifier = Modifier,
controller: BlurContentController,
content: @Composable BoxScope.() -> Unit,
) {
val transition = updateTransition(
targetState = controller.isBlurred.value,
)
val animatedBlur = transition.animateDp {
when (it) {
true -> controller.blurredRadius
else -> 0.dp
}
}
val animatedBackground = transition.animateColor {
when (it) {
true -> controller.scrimColor
else -> controller.scrimColor.copy(alpha = 0f)
}
}
Box(
modifier = modifier
) {
Box(
modifier = Modifier
.matchParentSize()
.blur(radius = animatedBlur.value, edgeTreatment = BlurredEdgeTreatment.Unbounded)
.drawWithContent {
drawContent()
drawRect(color = animatedBackground.value)
},
content = content,
)
}
}

View file

@ -18,7 +18,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable @Composable
fun DecoratedBox( fun DecoratedBox(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
border: Color = Color(0xFFDFDFDF), border: Color = Color(0xFF909090),
content: @Composable BoxScope.() -> Unit, content: @Composable BoxScope.() -> Unit,
) { ) {
Box( Box(

View file

@ -1,8 +1,15 @@
package com.pixelized.desktop.lwa.screen.characterSheet.detail package com.pixelized.desktop.lwa.screen.characterSheet.detail
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -30,6 +37,7 @@ import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -40,9 +48,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.desktop.lwa.LocalWindowController import com.pixelized.desktop.lwa.LocalWindowController
import com.pixelized.desktop.lwa.composable.blur.BlurContent
import com.pixelized.desktop.lwa.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.composable.overlay.BlurOverlay
import com.pixelized.desktop.lwa.composable.overlay.BlurOverlayViewModel
import com.pixelized.desktop.lwa.navigation.LocalScreenController import com.pixelized.desktop.lwa.navigation.LocalScreenController
import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialog import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialog
@ -97,24 +105,18 @@ fun CharacterSheetPage(
viewModel: CharacterSheetViewModel = viewModel { viewModel: CharacterSheetViewModel = viewModel {
CharacterSheetViewModel(savedStateHandle = createSavedStateHandle()) CharacterSheetViewModel(savedStateHandle = createSavedStateHandle())
}, },
overlayViewModel: BlurOverlayViewModel = viewModel { BlurOverlayViewModel() },
rollViewModel: RollViewModel = viewModel { RollViewModel() }, rollViewModel: RollViewModel = viewModel { RollViewModel() },
) { ) {
val window = LocalWindowController.current val window = LocalWindowController.current
val screen = LocalScreenController.current val screen = LocalScreenController.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val blurController = remember { BlurContentController() }
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) { ) {
BlurOverlay( BlurContent(
viewModel = overlayViewModel, controller = blurController,
overlay = {
RollPage(
viewModel = rollViewModel,
onDismissRequest = overlayViewModel::hide,
)
},
content = { content = {
viewModel.sheet.value?.let { sheet -> viewModel.sheet.value?.let { sheet ->
CharacterSheetPageContent( CharacterSheetPageContent(
@ -127,61 +129,91 @@ fun CharacterSheetPage(
) )
}, },
onDelete = { onDelete = {
blurController.show()
viewModel.showConfirmCharacterDeletionDialog() viewModel.showConfirmCharacterDeletionDialog()
}, },
onCharacteristic = { characteristic -> onCharacteristic = { characteristic ->
blurController.show()
rollViewModel.prepareRoll( rollViewModel.prepareRoll(
sheet = sheet, sheet = sheet,
characteristic = characteristic characteristic = characteristic
) )
overlayViewModel.show() viewModel.showRollOverlay()
}, },
onSubCharacteristic = { onSubCharacteristic = {
blurController.show()
viewModel.showSubCharacteristicDialog(id = it.id) viewModel.showSubCharacteristicDialog(id = it.id)
}, },
onSkill = { node -> onSkill = { node ->
blurController.show()
rollViewModel.prepareRoll(sheet = sheet, node = node) rollViewModel.prepareRoll(sheet = sheet, node = node)
overlayViewModel.show() viewModel.showRollOverlay()
}, },
onUseSkill = viewModel::onUseSkill, onUseSkill = viewModel::onUseSkill,
onRoll = { roll -> onRoll = { roll ->
blurController.show()
rollViewModel.prepareRoll(sheet = sheet, roll = roll) rollViewModel.prepareRoll(sheet = sheet, roll = roll)
overlayViewModel.show() viewModel.showRollOverlay()
}, },
) )
} }
}, },
) )
}
CharacterSheetDeleteConfirmationDialog( AnimatedContent(
dialog = viewModel.displayDeleteConfirmationDialog, targetState = viewModel.displayRollOverlay.value,
onConfirm = { transitionSpec = {
scope.launch { val enter = fadeIn() + slideInVertically { 64 }
viewModel.deleteCharacter(id = it.id) val exit = fadeOut() + slideOutVertically { 64 }
if (screen.popBackStack().not()) { enter togetherWith exit
window.closeWindows() },
} ) { roll ->
when (roll) {
true -> RollPage(
viewModel = rollViewModel,
onDismissRequest = {
blurController.hide()
viewModel.hideRollOverlay()
},
)
else -> Box(
modifier = Modifier.fillMaxSize()
)
} }
},
onDismissRequest = {
viewModel.hideConfirmCharacterDeletionDialog()
},
)
CharacterSheetStatDialog(
dialog = viewModel.statChangeDialog,
onConfirm = {
viewModel.changeSubCharacteristic(
characteristicId = it.id,
value = it.value().text.toIntOrNull() ?: 0,
)
viewModel.hideSubCharacteristicDialog()
},
onDismissRequest = {
viewModel.hideSubCharacteristicDialog()
} }
)
CharacterSheetDeleteConfirmationDialog(
dialog = viewModel.displayDeleteConfirmationDialog,
onConfirm = {
scope.launch {
viewModel.deleteCharacter(id = it.id)
if (screen.popBackStack().not()) {
window.closeWindows()
}
}
},
onDismissRequest = {
blurController.hide()
viewModel.hideConfirmCharacterDeletionDialog()
},
)
CharacterSheetStatDialog(
dialog = viewModel.statChangeDialog,
onConfirm = {
viewModel.changeSubCharacteristic(
characteristicId = it.id,
value = it.value().text.toIntOrNull() ?: 0,
)
viewModel.hideSubCharacteristicDialog()
},
onDismissRequest = {
blurController.hide()
viewModel.hideSubCharacteristicDialog()
}
)
}
} }
@Composable @Composable

View file

@ -17,6 +17,8 @@ import kotlinx.coroutines.runBlocking
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
private typealias CSDCDialogUio = CharacterSheetDeleteConfirmationDialogUio
class CharacterSheetViewModel( class CharacterSheetViewModel(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
) : ViewModel() { ) : ViewModel() {
@ -25,10 +27,11 @@ class CharacterSheetViewModel(
private val repository = CharacterSheetRepository private val repository = CharacterSheetRepository
private val factory = CharacterSheetFactory() private val factory = CharacterSheetFactory()
private val _displayDeleteConfirmationDialog = private val _displayDeleteConfirmationDialog = mutableStateOf<CSDCDialogUio?>(null)
mutableStateOf<CharacterSheetDeleteConfirmationDialogUio?>(null) val displayDeleteConfirmationDialog: State<CSDCDialogUio?> get() = _displayDeleteConfirmationDialog
val displayDeleteConfirmationDialog: State<CharacterSheetDeleteConfirmationDialogUio?>
get() = _displayDeleteConfirmationDialog private val _displayRollOverlay = mutableStateOf(false)
val displayRollOverlay: State<Boolean> get() = _displayRollOverlay
private val _statChangeDialog = mutableStateOf<StatChangeDialogUio?>(null) private val _statChangeDialog = mutableStateOf<StatChangeDialogUio?>(null)
val statChangeDialog: State<StatChangeDialogUio?> get() = _statChangeDialog val statChangeDialog: State<StatChangeDialogUio?> get() = _statChangeDialog
@ -140,4 +143,12 @@ class CharacterSheetViewModel(
repository.save(it) repository.save(it)
} }
} }
fun showRollOverlay() {
_displayRollOverlay.value = true
}
fun hideRollOverlay() {
_displayRollOverlay.value = false
}
} }

View file

@ -29,8 +29,8 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.theme.LwaColorPalette
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__cancel_action import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__confirm_action import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__confirm_action
@ -38,8 +38,6 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__title import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__title
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
private val DefaultScrimColor = Color.Black.copy(alpha = 0.6f)
@Stable @Stable
data class CharacterSheetDeleteConfirmationDialogUio( data class CharacterSheetDeleteConfirmationDialogUio(
val id: String, val id: String,
@ -52,15 +50,6 @@ fun CharacterSheetDeleteConfirmationDialog(
onConfirm: (CharacterSheetDeleteConfirmationDialogUio) -> Unit, onConfirm: (CharacterSheetDeleteConfirmationDialogUio) -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
AnimatedVisibility(
visible = dialog.value != null,
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = DefaultScrimColor),
)
}
AnimatedContent( AnimatedContent(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
targetState = dialog.value, targetState = dialog.value,

View file

@ -1,14 +1,12 @@
package com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog package com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -33,15 +31,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
private val DefaultScrimColor = Color.Black.copy(alpha = 0.6f)
@Stable @Stable
data class StatChangeDialogUio( data class StatChangeDialogUio(
val id: String, val id: String,
@ -57,15 +52,6 @@ fun CharacterSheetStatDialog(
onConfirm: (StatChangeDialogUio) -> Unit, onConfirm: (StatChangeDialogUio) -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
AnimatedVisibility(
visible = dialog.value != null,
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = DefaultScrimColor),
)
}
AnimatedContent( AnimatedContent(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
targetState = dialog.value, targetState = dialog.value,
@ -140,7 +126,7 @@ private fun Dialog(
}, },
singleLine = true, singleLine = true,
keyboardActions = KeyboardActions { onConfirm(dialog) }, keyboardActions = KeyboardActions { onConfirm(dialog) },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
placeholder = { dialog.placeholder }, placeholder = { dialog.placeholder },
onValueChange = dialog.onValueChange, onValueChange = dialog.onValueChange,
) )

View file

@ -0,0 +1,7 @@
package com.pixelized.desktop.lwa.theme
import androidx.compose.ui.graphics.Color
object LwaColorPalette {
val DefaultScrimColor = Color.Black.copy(alpha = 0.4f)
}