Refactor the Character Ribbon (levelup)
This commit is contained in:
parent
24fe030663
commit
6364201c6c
14 changed files with 457 additions and 331 deletions
|
|
@ -38,8 +38,9 @@ kotlin {
|
||||||
// composable component.
|
// composable component.
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
implementation(libs.coil.network.ktor)
|
implementation(libs.coil.network.ktor)
|
||||||
// implementation("com.mikepenz.hypnoticcanvas:hypnoticcanvas:0.3.0")
|
// Shader
|
||||||
// implementation("com.mikepenz.hypnoticcanvas:hypnoticcanvas-shaders:0.3.0")
|
implementation(libs.hypnoticcanvas)
|
||||||
|
implementation(libs.hypnoticcanvas.shaders)
|
||||||
// network
|
// network
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
implementation(libs.ktor.serialization.json)
|
implementation(libs.ktor.serialization.json)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.pixelized.desktop.lwa.LocalBlurController
|
import com.pixelized.desktop.lwa.LocalBlurController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||||
|
|
@ -32,6 +33,7 @@ object LwaDialogDefault {
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> LwaDialog(
|
fun <T> LwaDialog(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
properties: DialogProperties = DialogProperties(),
|
||||||
blur: BlurContentController? = LocalBlurController.current,
|
blur: BlurContentController? = LocalBlurController.current,
|
||||||
paddings: PaddingValues = LwaDialogDefault.paddings,
|
paddings: PaddingValues = LwaDialogDefault.paddings,
|
||||||
color: Color = MaterialTheme.colors.surface,
|
color: Color = MaterialTheme.colors.surface,
|
||||||
|
|
@ -52,6 +54,7 @@ fun <T> LwaDialog(
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
|
properties = properties,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
content = {
|
content = {
|
||||||
Box(
|
Box(
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ 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
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
|
@ -19,22 +18,21 @@ import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.minimumInteractiveComponentSize
|
import androidx.compose.material.minimumInteractiveComponentSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.pixelized.desktop.lwa.LocalBlurController
|
import androidx.compose.ui.zIndex
|
||||||
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.character.LwaDialog
|
||||||
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMFilterHeader
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMFilterHeader
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import com.pixelized.desktop.lwa.utils.extention.calculatePaddings
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
||||||
|
|
@ -52,113 +50,112 @@ data class CharacterSheetAlterationDialogUio(
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterSheetAlterationDialog(
|
fun CharacterSheetAlterationDialog(
|
||||||
blur: BlurContentController? = LocalBlurController.current,
|
|
||||||
dialog: State<CharacterSheetAlterationDialogUio?>,
|
dialog: State<CharacterSheetAlterationDialogUio?>,
|
||||||
onTag: (String) -> Unit,
|
onTag: (String) -> Unit,
|
||||||
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
|
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
dialog.value?.let {
|
LwaDialog(
|
||||||
|
properties = DialogProperties(
|
||||||
blur?.let {
|
usePlatformDefaultWidth = false,
|
||||||
DisposableEffect("LwaDialog") {
|
usePlatformInsets = false,
|
||||||
blur.show()
|
),
|
||||||
onDispose {
|
state = dialog,
|
||||||
blur.hide()
|
onDismissRequest = onDismissRequest,
|
||||||
}
|
onConfirm = onDismissRequest,
|
||||||
}
|
content = {
|
||||||
|
CharacterSheetAlterationContent(
|
||||||
|
dialog = it,
|
||||||
|
onTag = onTag,
|
||||||
|
onAlteration = onAlteration,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
Dialog(
|
|
||||||
properties = DialogProperties(
|
|
||||||
usePlatformDefaultWidth = false,
|
|
||||||
usePlatformInsets = false,
|
|
||||||
),
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
content = {
|
|
||||||
CharacterSheetAlterationContent(
|
|
||||||
dialog = it,
|
|
||||||
onTag = onTag,
|
|
||||||
onAlteration = onAlteration,
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterSheetAlterationContent(
|
fun CharacterSheetAlterationContent(
|
||||||
dialog: CharacterSheetAlterationDialogUio,
|
dialog: CharacterSheetAlterationDialogUio,
|
||||||
|
paddingValues: PaddingValues = MaterialTheme.lwa.dimen.paddingValues,
|
||||||
onTag: (String) -> Unit,
|
onTag: (String) -> Unit,
|
||||||
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
|
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Surface(
|
val padding = MaterialTheme.lwa.dimen.paddingValue
|
||||||
|
val (start, _, end, _) = paddingValues.calculatePaddings()
|
||||||
|
|
||||||
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.width(width = 128.dp * 4)
|
.width(width = 128.dp * 4)
|
||||||
.padding(vertical = 16.dp),
|
|
||||||
) {
|
) {
|
||||||
Column(
|
// Add a surface and a zIndex() to avoid the animation coming from : .animateItem() (see below)
|
||||||
modifier = Modifier.fillMaxSize(),
|
// to draw over this part of the dialog.
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.zIndex(1f)
|
||||||
) {
|
) {
|
||||||
Row(
|
Column {
|
||||||
modifier = Modifier.padding(start = 16.dp).fillMaxWidth(),
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
modifier = Modifier
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
.padding(start = start)
|
||||||
) {
|
.fillMaxWidth(),
|
||||||
Text(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
style = MaterialTheme.typography.h6,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
color = MaterialTheme.colors.onSurface,
|
|
||||||
text = dialog.characterName,
|
|
||||||
)
|
|
||||||
IconButton(
|
|
||||||
onClick = onDismissRequest,
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
Text(
|
||||||
painter = painterResource(Res.drawable.ic_close_24dp),
|
style = MaterialTheme.typography.h6,
|
||||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
color = MaterialTheme.colors.onSurface,
|
||||||
contentDescription = null,
|
text = dialog.characterName,
|
||||||
)
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = onDismissRequest,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(Res.drawable.ic_close_24dp),
|
||||||
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
GMFilterHeader(
|
||||||
|
filter = dialog.filter,
|
||||||
|
padding = padding,
|
||||||
|
tags = dialog.tags.collectAsStateWithLifecycle(),
|
||||||
|
onTag = onTag,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
GMFilterHeader(
|
}
|
||||||
filter = dialog.filter,
|
LazyColumn(
|
||||||
tags = dialog.tags.collectAsState(),
|
modifier = Modifier
|
||||||
onTag = onTag,
|
.fillMaxWidth()
|
||||||
)
|
.weight(weight = 1f, fill = true),
|
||||||
LazyColumn(
|
contentPadding = paddingValues,
|
||||||
modifier = Modifier
|
verticalArrangement = Arrangement.spacedBy(space = padding),
|
||||||
.fillMaxWidth()
|
) {
|
||||||
.weight(weight = 1f),
|
items(
|
||||||
contentPadding = PaddingValues(start = 8.dp, end = 8.dp, bottom = 8.dp),
|
items = dialog.alterations,
|
||||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
|
key = { it.id },
|
||||||
) {
|
) { alteration ->
|
||||||
items(
|
AlterationToggleItem(
|
||||||
items = dialog.alterations,
|
modifier = Modifier
|
||||||
key = { it.id }
|
.animateItem()
|
||||||
) { alteration ->
|
.background(
|
||||||
AlterationToggleItem(
|
color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
|
||||||
modifier = Modifier
|
shape = MaterialTheme.lwa.shapes.base.small,
|
||||||
.animateItem()
|
)
|
||||||
.background(
|
.minimumInteractiveComponentSize()
|
||||||
color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
|
.padding(start = start, end = end),
|
||||||
shape = MaterialTheme.lwa.shapes.base.small,
|
alteration = alteration,
|
||||||
)
|
onAlteration = {
|
||||||
.minimumInteractiveComponentSize()
|
onAlteration(
|
||||||
.padding(horizontal = 8.dp),
|
dialog.characterSheetId,
|
||||||
alteration = alteration,
|
alteration.id,
|
||||||
onAlteration = {
|
alteration.active,
|
||||||
onAlteration(
|
)
|
||||||
dialog.characterSheetId,
|
},
|
||||||
alteration.id,
|
onTag = onTag,
|
||||||
alteration.active,
|
)
|
||||||
)
|
|
||||||
},
|
|
||||||
onTag = onTag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ import androidx.compose.ui.unit.Density
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
|
||||||
import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController
|
import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialog
|
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialog
|
||||||
|
|
@ -143,9 +142,6 @@ fun CampaignScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLevelUp = {
|
|
||||||
screen.navigateToLevelScreen(characterSheetId = it)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
rightPanel = {
|
rightPanel = {
|
||||||
|
|
@ -169,9 +165,6 @@ fun CampaignScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLevelUp = {
|
|
||||||
screen.navigateToLevelScreen(characterSheetId = it)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
leftOverlay = {
|
leftOverlay = {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.PointerMatcher
|
import androidx.compose.foundation.PointerMatcher
|
||||||
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.onClick
|
import androidx.compose.foundation.onClick
|
||||||
|
|
@ -13,13 +19,22 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.pointer.PointerButton
|
import androidx.compose.ui.input.pointer.PointerButton
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.lordcodes.turtle.Arguments
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlteration
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlteration
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortrait
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortrait
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRoll
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRoll
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonStats
|
||||||
|
import com.pixelized.desktop.lwa.ui.shaders.packs.GoldenFlow
|
||||||
|
import com.pixelized.desktop.lwa.ui.shaders.shaderBorder
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import com.pixelized.desktop.lwa.utils.extention.invert
|
import com.pixelized.desktop.lwa.utils.extention.invert
|
||||||
|
|
||||||
|
|
@ -29,10 +44,11 @@ fun CharacterRibbon(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
layoutDirection: LayoutDirection,
|
layoutDirection: LayoutDirection,
|
||||||
viewModel: CharacterRibbonViewModel,
|
viewModel: CharacterRibbonViewModel,
|
||||||
|
shape: Shape = MaterialTheme.lwa.shapes.portrait,
|
||||||
|
size: DpSize = MaterialTheme.lwa.dimen.portrait.minimized,
|
||||||
padding: PaddingValues = MaterialTheme.lwa.dimen.paddingValues,
|
padding: PaddingValues = MaterialTheme.lwa.dimen.paddingValues,
|
||||||
onCharacterLeftClick: (characterSheetId: String) -> Unit,
|
onCharacterLeftClick: (characterSheetId: String) -> Unit,
|
||||||
onCharacterRightClick: (characterSheetId: String) -> Unit,
|
onCharacterRightClick: (characterSheetId: String) -> Unit,
|
||||||
onLevelUp: (characterSheetId: String) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val characters = viewModel.characters.collectAsState()
|
val characters = viewModel.characters.collectAsState()
|
||||||
|
|
||||||
|
|
@ -53,6 +69,7 @@ fun CharacterRibbon(
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.animateItem()
|
.animateItem()
|
||||||
|
.clip(shape = shape)
|
||||||
.graphicsLayer { if (it.hideOverruled) this.alpha = 0.3f }
|
.graphicsLayer { if (it.hideOverruled) this.alpha = 0.3f }
|
||||||
.onClick(
|
.onClick(
|
||||||
matcher = PointerMatcher.mouse(PointerButton.Primary),
|
matcher = PointerMatcher.mouse(PointerButton.Primary),
|
||||||
|
|
@ -66,19 +83,38 @@ fun CharacterRibbon(
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
CharacterRibbonPortrait(
|
CharacterRibbonPortrait(
|
||||||
|
size = size,
|
||||||
character = it.portrait,
|
character = it.portrait,
|
||||||
onLevelUp = { onLevelUp(it.characterSheetId) },
|
|
||||||
)
|
)
|
||||||
CompositionLocalProvider(
|
Column(
|
||||||
LocalLayoutDirection provides layoutDirection.invert
|
modifier = Modifier.size(size),
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
CharacterRibbonAlteration(
|
CompositionLocalProvider(
|
||||||
|
LocalLayoutDirection provides layoutDirection.invert
|
||||||
|
) {
|
||||||
|
CharacterRibbonAlteration(
|
||||||
|
alterations = it.alterations,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CharacterRibbonStats(
|
||||||
status = it.status,
|
status = it.status,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CharacterRibbonRoll(
|
CharacterRibbonRoll(
|
||||||
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
||||||
)
|
)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = it.levelUp,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut()
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(size)
|
||||||
|
.shaderBorder(width = 2.dp, shape = shape, shader = GoldenFlow)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||||
import com.pixelized.desktop.lwa.ui.composable.tooltip.BasicTooltipUio
|
import com.pixelized.desktop.lwa.ui.composable.tooltip.BasicTooltipUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlterationUio
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlterationUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortraitUio
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortraitUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonStats
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonStatsUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonUio
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonUio
|
||||||
import com.pixelized.desktop.lwa.utils.extention.foldInto
|
import com.pixelized.desktop.lwa.utils.extention.foldInto
|
||||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||||
|
|
@ -35,20 +37,25 @@ class CharacterRibbonFactory(
|
||||||
characterSheetId = characterSheet.id,
|
characterSheetId = characterSheet.id,
|
||||||
hideOverruled = hideOverruled,
|
hideOverruled = hideOverruled,
|
||||||
enableDetail = enableCharacterSheet,
|
enableDetail = enableCharacterSheet,
|
||||||
|
levelUp = alteredCharacterSheet.shouldLevelUp,
|
||||||
portrait = CharacterRibbonPortraitUio(
|
portrait = CharacterRibbonPortraitUio(
|
||||||
portrait = alteredCharacterSheet.thumbnail,
|
portrait = alteredCharacterSheet.thumbnail,
|
||||||
name = alteredCharacterSheet.name,
|
name = alteredCharacterSheet.name,
|
||||||
levelUp = alteredCharacterSheet.shouldLevelUp,
|
damagePercent = takeIf { enableCharacterStats }?.let {
|
||||||
stats = takeIf { enableCharacterStats }?.let {
|
val maxHp = alteredCharacterSheet.maxHp.toFloat()
|
||||||
CharacterRibbonPortraitUio.StatsDetail(
|
val damage = alteredCharacterSheet.damage.toFloat()
|
||||||
hp = alteredCharacterSheet.maxHp - alteredCharacterSheet.damage,
|
1f - ((maxHp - damage) / maxHp).coerceIn(0f, 1f)
|
||||||
maxHp = alteredCharacterSheet.maxHp,
|
|
||||||
pp = alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue,
|
|
||||||
maxPp = alteredCharacterSheet.maxPp,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
status = takeIf { enableCharacterStatus }?.let {
|
status = takeIf { enableCharacterStats }?.let {
|
||||||
|
CharacterRibbonStatsUio(
|
||||||
|
hp = alteredCharacterSheet.maxHp - alteredCharacterSheet.damage,
|
||||||
|
maxHp = alteredCharacterSheet.maxHp,
|
||||||
|
pp = alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue,
|
||||||
|
maxPp = alteredCharacterSheet.maxPp,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
alterations = takeIf { enableCharacterStatus }?.let {
|
||||||
alterations.map { alteration ->
|
alterations.map { alteration ->
|
||||||
CharacterRibbonAlterationUio(
|
CharacterRibbonAlterationUio(
|
||||||
icon = alteration.metadata.icon ?: DEFAULT_ICON,
|
icon = alteration.metadata.icon ?: DEFAULT_ICON,
|
||||||
|
|
|
||||||
|
|
@ -13,22 +13,19 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BloodOverlay(
|
fun BloodOverlay(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
bloodColor: Color = MaterialTheme.lwa.colorScheme.portrait.blood,
|
bloodColor: Color = MaterialTheme.lwa.colorScheme.portrait.blood,
|
||||||
maxHp: Float,
|
damagePercent: Float,
|
||||||
hp: Float,
|
|
||||||
) {
|
) {
|
||||||
val animatedRatio = animateFloatAsState(
|
val animatedRatio = animateFloatAsState(
|
||||||
targetValue = min(maxHp, max(0f, (maxHp - hp) / maxHp)),
|
targetValue = damagePercent,
|
||||||
animationSpec = tween(durationMillis = 350, easing = EaseOutCirc)
|
animationSpec = tween(durationMillis = 350, easing = EaseOutCirc)
|
||||||
)
|
)
|
||||||
val animatedColor = animateColorAsState(
|
val animatedColor = animateColorAsState(
|
||||||
targetValue = bloodColor.copy(alpha = ((maxHp - hp) / maxHp) / 4f + .25f)
|
targetValue = bloodColor.copy(alpha = damagePercent / 4f + .25f)
|
||||||
)
|
)
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.togetherWith
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.TooltipPlacement
|
import androidx.compose.foundation.TooltipPlacement
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
|
@ -42,19 +37,18 @@ data class CharacterRibbonAlterationUio(
|
||||||
fun CharacterRibbonAlteration(
|
fun CharacterRibbonAlteration(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
width: Dp = MaterialTheme.lwa.dimen.portrait.minimized.width,
|
width: Dp = MaterialTheme.lwa.dimen.portrait.minimized.width,
|
||||||
status: List<List<CharacterRibbonAlterationUio>>,
|
alterations: List<List<CharacterRibbonAlterationUio>>,
|
||||||
) {
|
) {
|
||||||
val direction = LocalLayoutDirection.current
|
val direction = LocalLayoutDirection.current
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.animateContentSize()
|
|
||||||
.width(width = width)
|
.width(width = width)
|
||||||
.then(other = modifier),
|
.then(other = modifier),
|
||||||
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||||
) {
|
) {
|
||||||
status.forEach { columns ->
|
alterations.forEach { columns ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.animateContentSize(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(space = 2.dp),
|
verticalArrangement = Arrangement.spacedBy(space = 2.dp),
|
||||||
) {
|
) {
|
||||||
columns.forEach {
|
columns.forEach {
|
||||||
|
|
@ -70,20 +64,15 @@ fun CharacterRibbonAlteration(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
AnimatedContent(
|
LwaAsyncImage(
|
||||||
targetState = it.icon,
|
modifier = Modifier.size(24.dp),
|
||||||
transitionSpec = { fadeIn() togetherWith fadeOut() },
|
model = ImageRequest.Builder(context = PlatformContext.INSTANCE)
|
||||||
) { icon ->
|
.data(data = it.icon)
|
||||||
LwaAsyncImage(
|
.size(size = 48)
|
||||||
modifier = Modifier.size(24.dp),
|
.build(),
|
||||||
model = ImageRequest.Builder(context = PlatformContext.INSTANCE)
|
filterQuality = FilterQuality.High,
|
||||||
.data(data = icon)
|
contentDescription = null,
|
||||||
.size(size = 48)
|
)
|
||||||
.build(),
|
|
||||||
filterQuality = FilterQuality.High,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,54 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material.Icon
|
|
||||||
import androidx.compose.material.IconButton
|
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.FilterQuality
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.pixelized.desktop.lwa.ui.composable.image.LwaAsyncImage
|
import com.pixelized.desktop.lwa.ui.composable.image.LwaAsyncImage
|
||||||
import com.pixelized.desktop.lwa.ui.composable.shapes.ArrowShape
|
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
|
||||||
import org.jetbrains.compose.resources.painterResource
|
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class CharacterRibbonPortraitUio(
|
data class CharacterRibbonPortraitUio(
|
||||||
val portrait: String?,
|
val portrait: String?,
|
||||||
val name: String,
|
val name: String,
|
||||||
val levelUp: Boolean,
|
val damagePercent: Float?,
|
||||||
val stats: StatsDetail?,
|
)
|
||||||
) {
|
|
||||||
@Stable
|
|
||||||
data class StatsDetail(
|
|
||||||
val hp: Int,
|
|
||||||
val maxHp: Int,
|
|
||||||
val pp: Int,
|
|
||||||
val maxPp: Int,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterRibbonPortrait(
|
fun CharacterRibbonPortrait(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
background: Color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
|
||||||
|
overlay: Brush = MaterialTheme.lwa.colorScheme.portraitBackgroundBrush,
|
||||||
size: DpSize = MaterialTheme.lwa.dimen.portrait.minimized,
|
size: DpSize = MaterialTheme.lwa.dimen.portrait.minimized,
|
||||||
levelUpOffset: Dp = 9.dp,
|
|
||||||
character: CharacterRibbonPortraitUio,
|
character: CharacterRibbonPortraitUio,
|
||||||
onLevelUp: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val colorScheme = MaterialTheme.lwa.colorScheme
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(size = size)
|
.size(size = size)
|
||||||
.clip(shape = MaterialTheme.lwa.shapes.portrait)
|
.background(color = background)
|
||||||
.background(color = colorScheme.elevated.base1dp)
|
.drawWithContent {
|
||||||
|
drawContent()
|
||||||
|
drawRect(brush = overlay)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = character.portrait,
|
targetState = character.portrait,
|
||||||
|
|
@ -87,95 +64,10 @@ fun CharacterRibbonPortrait(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
character.damagePercent?.let {
|
||||||
modifier = Modifier
|
|
||||||
.align(alignment = Alignment.TopEnd)
|
|
||||||
.offset(x = levelUpOffset, y = -levelUpOffset),
|
|
||||||
visible = character.levelUp,
|
|
||||||
enter = fadeIn(),
|
|
||||||
exit = fadeOut(),
|
|
||||||
) {
|
|
||||||
IconButton(
|
|
||||||
onClick = onLevelUp,
|
|
||||||
) {
|
|
||||||
ArrowShape(
|
|
||||||
color = MaterialTheme.lwa.colorScheme.portrait.levelUp,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
character.stats?.let { stats ->
|
|
||||||
|
|
||||||
BloodOverlay(
|
BloodOverlay(
|
||||||
maxHp = stats.maxHp.toFloat(),
|
damagePercent = it,
|
||||||
hp = stats.hp.toFloat(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.drawWithContent {
|
|
||||||
drawRect(brush = colorScheme.portraitBackgroundBrush)
|
|
||||||
drawContent()
|
|
||||||
}
|
|
||||||
.padding(vertical = 2.dp, horizontal = 4.dp),
|
|
||||||
verticalArrangement = Arrangement.aligned(alignment = Alignment.Bottom),
|
|
||||||
) {
|
|
||||||
Row {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(12.dp)
|
|
||||||
.offset(y = 2.dp),
|
|
||||||
painter = painterResource(Res.drawable.ic_heart_24dp),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier.width(width = 2.dp),
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.lwa.typography.portrait.value,
|
|
||||||
text = "${stats.hp}",
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.lwa.typography.portrait.separator,
|
|
||||||
text = "/",
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.lwa.typography.portrait.max,
|
|
||||||
text = "${stats.maxHp}",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(12.dp)
|
|
||||||
.offset(y = 2.dp),
|
|
||||||
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier.width(width = 2.dp),
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.lwa.typography.portrait.value,
|
|
||||||
text = "${stats.pp}",
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.lwa.typography.portrait.separator,
|
|
||||||
text = "/",
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.alignByBaseline(),
|
|
||||||
style = MaterialTheme.lwa.typography.portrait.max,
|
|
||||||
text = "${stats.maxPp}",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
||||||
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class CharacterRibbonStatsUio(
|
||||||
|
val hp: Int,
|
||||||
|
val maxHp: Int,
|
||||||
|
val pp: Int,
|
||||||
|
val maxPp: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CharacterRibbonStats(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
status: CharacterRibbonStatsUio?,
|
||||||
|
) {
|
||||||
|
status?.let { status ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 2.dp, horizontal = 4.dp)
|
||||||
|
.then(other = modifier),
|
||||||
|
verticalArrangement = Arrangement.aligned(alignment = Alignment.Bottom),
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(12.dp)
|
||||||
|
.offset(y = 2.dp),
|
||||||
|
painter = painterResource(Res.drawable.ic_heart_24dp),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.width(width = 2.dp),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.portrait.value,
|
||||||
|
text = "${status.hp}",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.portrait.separator,
|
||||||
|
text = "/",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.portrait.max,
|
||||||
|
text = "${status.maxHp}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(size = 12.dp)
|
||||||
|
.offset(y = 2.dp),
|
||||||
|
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.width(width = 2.dp),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.portrait.value,
|
||||||
|
text = "${status.pp}",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.portrait.separator,
|
||||||
|
text = "/",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.portrait.max,
|
||||||
|
text = "${status.maxPp}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,8 @@ class CharacterRibbonUio(
|
||||||
val characterSheetId: String,
|
val characterSheetId: String,
|
||||||
val hideOverruled: Boolean,
|
val hideOverruled: Boolean,
|
||||||
val enableDetail: Boolean,
|
val enableDetail: Boolean,
|
||||||
|
val levelUp: Boolean,
|
||||||
val portrait: CharacterRibbonPortraitUio,
|
val portrait: CharacterRibbonPortraitUio,
|
||||||
val status: List<List<CharacterRibbonAlterationUio>>,
|
val status: CharacterRibbonStatsUio?,
|
||||||
|
val alterations: List<List<CharacterRibbonAlterationUio>>,
|
||||||
)
|
)
|
||||||
|
|
@ -1,67 +1,125 @@
|
||||||
//package com.pixelized.desktop.lwa.ui.shaders
|
package com.pixelized.desktop.lwa.ui.shaders
|
||||||
//
|
|
||||||
//import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
|
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
|
||||||
//import androidx.compose.runtime.Composable
|
import androidx.compose.foundation.border
|
||||||
//import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.Composable
|
||||||
//import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.DisposableEffect
|
||||||
//import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
//import androidx.compose.runtime.remember
|
import androidx.compose.runtime.getValue
|
||||||
//import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.mutableStateOf
|
||||||
//import androidx.compose.ui.Modifier
|
import androidx.compose.runtime.produceState
|
||||||
//import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.runtime.remember
|
||||||
//import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.runtime.setValue
|
||||||
//import androidx.compose.ui.geometry.Size
|
import androidx.compose.ui.Modifier
|
||||||
//import androidx.compose.ui.graphics.BlendMode
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
//import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.geometry.Size
|
||||||
//import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.BlendMode
|
||||||
//import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.graphics.Brush
|
||||||
//import com.mikepenz.hypnoticcanvas.NonAndroidRuntimeEffect
|
import androidx.compose.ui.graphics.Color
|
||||||
//import com.mikepenz.hypnoticcanvas.shaders.Shader
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
//import com.mikepenz.hypnoticcanvas.utils.round
|
import androidx.compose.ui.graphics.Shape
|
||||||
//
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
//@Composable
|
import androidx.compose.ui.unit.Dp
|
||||||
//fun Modifier.shaderContent(
|
import androidx.compose.ui.unit.dp
|
||||||
// shader: Shader,
|
import com.mikepenz.hypnoticcanvas.NonAndroidRuntimeEffect
|
||||||
// speed: Float = 1f,
|
import com.mikepenz.hypnoticcanvas.shaders.Shader
|
||||||
// fallback: () -> Brush = {
|
import com.mikepenz.hypnoticcanvas.utils.round
|
||||||
// Brush.horizontalGradient(listOf(Color.Transparent, Color.Transparent))
|
|
||||||
// },
|
@Composable
|
||||||
//): Modifier {
|
fun Modifier.shaderContent(
|
||||||
// val runtimeEffect = remember(shader) { NonAndroidRuntimeEffect(shader) }
|
shader: Shader,
|
||||||
// var size: Size by remember { mutableStateOf(Size(-1f, -1f)) }
|
speed: Float = 1f,
|
||||||
// val speedModifier = shader.speedModifier
|
fallback: () -> Brush = {
|
||||||
//
|
Brush.horizontalGradient(listOf(Color.Transparent, Color.Transparent))
|
||||||
// val time by if (runtimeEffect.supported) {
|
},
|
||||||
// var startMillis = remember(shader) { -1L }
|
): Modifier {
|
||||||
// produceState(0f, speedModifier) {
|
val runtimeEffect = remember(shader) { NonAndroidRuntimeEffect(shader) }
|
||||||
// while (true) {
|
var size: Size by remember { mutableStateOf(Size(-1f, -1f)) }
|
||||||
// withInfiniteAnimationFrameMillis {
|
val speedModifier = shader.speedModifier
|
||||||
// if (startMillis < 0) startMillis = it
|
|
||||||
// value = ((it - startMillis) / 16.6f) / 10f
|
val time by if (runtimeEffect.supported) {
|
||||||
// }
|
var startMillis = remember(shader) { -1L }
|
||||||
// }
|
produceState(0f, speedModifier) {
|
||||||
// }
|
while (true) {
|
||||||
// } else {
|
withInfiniteAnimationFrameMillis {
|
||||||
// mutableStateOf(-1f)
|
if (startMillis < 0) startMillis = it
|
||||||
// }
|
value = ((it - startMillis) / 16.6f) / 10f
|
||||||
//
|
}
|
||||||
// return this then Modifier
|
}
|
||||||
// .onGloballyPositioned {
|
}
|
||||||
// size = Size(it.size.width.toFloat(), it.size.height.toFloat())
|
} else {
|
||||||
// }
|
mutableStateOf(-1f)
|
||||||
// .drawWithContent {
|
}
|
||||||
// drawContent()
|
|
||||||
// // set uniforms for the shaders
|
return this then Modifier
|
||||||
// runtimeEffect.update(
|
.onGloballyPositioned {
|
||||||
// shader = shader,
|
size = Size(it.size.width.toFloat(), it.size.height.toFloat())
|
||||||
// time = (time * speed * speedModifier).round(3),
|
}
|
||||||
// width = size.width,
|
.drawWithContent {
|
||||||
// height = size.height
|
drawContent()
|
||||||
// )
|
// set uniforms for the shaders
|
||||||
// if (runtimeEffect.ready) {
|
runtimeEffect.update(
|
||||||
// drawRect(brush = runtimeEffect.build(), blendMode = BlendMode.SrcAtop)
|
shader = shader,
|
||||||
// } else {
|
time = (time * speed * speedModifier).round(3),
|
||||||
// drawRect(brush = fallback())
|
width = size.width,
|
||||||
// }
|
height = size.height
|
||||||
// }
|
)
|
||||||
//}
|
if (runtimeEffect.ready) {
|
||||||
|
drawRect(brush = runtimeEffect.build(), blendMode = BlendMode.SrcAtop)
|
||||||
|
} else {
|
||||||
|
drawRect(brush = fallback())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Modifier.shaderBorder(
|
||||||
|
width: Dp = 1.dp,
|
||||||
|
shape: Shape = RectangleShape,
|
||||||
|
shader: Shader,
|
||||||
|
speed: Float = 1f,
|
||||||
|
fallback: () -> Brush = {
|
||||||
|
Brush.horizontalGradient(listOf(Color.Transparent, Color.Transparent))
|
||||||
|
},
|
||||||
|
): Modifier {
|
||||||
|
val runtimeEffect = remember(shader) { NonAndroidRuntimeEffect(shader) }
|
||||||
|
var size: Size by remember { mutableStateOf(Size(-1f, -1f)) }
|
||||||
|
val speedModifier = shader.speedModifier
|
||||||
|
|
||||||
|
val time by if (runtimeEffect.supported) {
|
||||||
|
var startMillis = remember(shader) { -1L }
|
||||||
|
produceState(0f, speedModifier) {
|
||||||
|
while (true) {
|
||||||
|
withInfiniteAnimationFrameMillis {
|
||||||
|
if (startMillis < 0) startMillis = it
|
||||||
|
value = ((it - startMillis) / 16.6f) / 10f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mutableStateOf(-1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this then Modifier
|
||||||
|
.onGloballyPositioned {
|
||||||
|
size = Size(it.size.width.toFloat(), it.size.height.toFloat())
|
||||||
|
}
|
||||||
|
.border(
|
||||||
|
width = width,
|
||||||
|
shape = shape,
|
||||||
|
brush = run {
|
||||||
|
// set uniforms for the shaders
|
||||||
|
runtimeEffect.update(
|
||||||
|
shader = shader,
|
||||||
|
time = (time * speed * speedModifier).round(3),
|
||||||
|
width = size.width,
|
||||||
|
height = size.height
|
||||||
|
)
|
||||||
|
if (runtimeEffect.ready) {
|
||||||
|
runtimeEffect.build()
|
||||||
|
} else {
|
||||||
|
fallback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.shaders.packs
|
||||||
|
|
||||||
|
import com.mikepenz.hypnoticcanvas.shaders.Shader
|
||||||
|
|
||||||
|
data object GoldenFlow : Shader {
|
||||||
|
override val name: String
|
||||||
|
get() = ""
|
||||||
|
override val authorName: String
|
||||||
|
get() = ""
|
||||||
|
override val authorUrl: String
|
||||||
|
get() = ""
|
||||||
|
override val credit: String
|
||||||
|
get() = ""
|
||||||
|
override val license: String
|
||||||
|
get() = ""
|
||||||
|
override val licenseUrl: String
|
||||||
|
get() = ""
|
||||||
|
|
||||||
|
override val sksl: String
|
||||||
|
get() = """
|
||||||
|
uniform float uTime;
|
||||||
|
uniform vec3 uResolution;
|
||||||
|
|
||||||
|
vec4 main(vec2 fragCoord) {
|
||||||
|
vec2 q = 7.0 * (fragCoord.xy - 0.5 * uResolution.xy) / max(uResolution.x, uResolution.y);
|
||||||
|
|
||||||
|
float i = 1.0;
|
||||||
|
for(int iter = 0; iter < 40; iter++)
|
||||||
|
{
|
||||||
|
vec2 o = q;
|
||||||
|
o.x += (0.5 / i) * cos(i * q.y + uTime * 0.297 + 0.03 * i) + 1.3;
|
||||||
|
o.y += (0.5 / i) * cos(i * q.x + uTime * 0.414 + 0.03 * (i + 10.0)) + 1.9;
|
||||||
|
q = o;
|
||||||
|
i *= 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 col = vec3(0.5 * sin(3.0 * q.x) + 0.5, 0.5 * sin(3.0 * q.y) + 0.5, sin(1.3 * q.x + 1.7 * q.y));
|
||||||
|
float f = 0.43 * (col.x + col.y + col.z);
|
||||||
|
|
||||||
|
return vec4(f + 0.6, 0.2 + 0.75 * f, 0.2, 0.5);
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ coil = "3.1.0"
|
||||||
zoomable = "2.7.0"
|
zoomable = "2.7.0"
|
||||||
ui-graphics-android = "1.7.8"
|
ui-graphics-android = "1.7.8"
|
||||||
buildkonfig = "0.17.0"
|
buildkonfig = "0.17.0"
|
||||||
|
shader = "0.3.0"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
||||||
|
|
@ -37,6 +38,9 @@ kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-
|
||||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||||
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
|
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
|
||||||
engawapg-zoomable = { module = "net.engawapg.lib:zoomable", version.ref = "zoomable" }
|
engawapg-zoomable = { module = "net.engawapg.lib:zoomable", version.ref = "zoomable" }
|
||||||
|
# Shader
|
||||||
|
hypnoticcanvas = { module = "com.mikepenz.hypnoticcanvas:hypnoticcanvas", version.ref = "shader" }
|
||||||
|
hypnoticcanvas-shaders = { module = "com.mikepenz.hypnoticcanvas:hypnoticcanvas-shaders", version.ref = "shader" }
|
||||||
# Injection with Koin
|
# Injection with Koin
|
||||||
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue