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
fun DecoratedBox(
modifier: Modifier = Modifier,
border: Color = Color(0xFFDFDFDF),
border: Color = Color(0xFF909090),
content: @Composable BoxScope.() -> Unit,
) {
Box(

View file

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

View file

@ -17,6 +17,8 @@ import kotlinx.coroutines.runBlocking
import kotlin.math.max
import kotlin.math.min
private typealias CSDCDialogUio = CharacterSheetDeleteConfirmationDialogUio
class CharacterSheetViewModel(
savedStateHandle: SavedStateHandle,
) : ViewModel() {
@ -25,10 +27,11 @@ class CharacterSheetViewModel(
private val repository = CharacterSheetRepository
private val factory = CharacterSheetFactory()
private val _displayDeleteConfirmationDialog =
mutableStateOf<CharacterSheetDeleteConfirmationDialogUio?>(null)
val displayDeleteConfirmationDialog: State<CharacterSheetDeleteConfirmationDialogUio?>
get() = _displayDeleteConfirmationDialog
private val _displayDeleteConfirmationDialog = mutableStateOf<CSDCDialogUio?>(null)
val displayDeleteConfirmationDialog: State<CSDCDialogUio?> get() = _displayDeleteConfirmationDialog
private val _displayRollOverlay = mutableStateOf(false)
val displayRollOverlay: State<Boolean> get() = _displayRollOverlay
private val _statChangeDialog = mutableStateOf<StatChangeDialogUio?>(null)
val statChangeDialog: State<StatChangeDialogUio?> get() = _statChangeDialog
@ -140,4 +143,12 @@ class CharacterSheetViewModel(
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.theme.LwaColorPalette
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__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 org.jetbrains.compose.resources.stringResource
private val DefaultScrimColor = Color.Black.copy(alpha = 0.6f)
@Stable
data class CharacterSheetDeleteConfirmationDialogUio(
val id: String,
@ -52,15 +50,6 @@ fun CharacterSheetDeleteConfirmationDialog(
onConfirm: (CharacterSheetDeleteConfirmationDialogUio) -> Unit,
onDismissRequest: () -> Unit,
) {
AnimatedVisibility(
visible = dialog.value != null,
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = DefaultScrimColor),
)
}
AnimatedContent(
modifier = Modifier.fillMaxSize(),
targetState = dialog.value,

View file

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