New UI.
This commit is contained in:
parent
d84bc9bbef
commit
0c613c5b72
25 changed files with 947 additions and 23 deletions
|
|
@ -29,6 +29,9 @@ kotlin {
|
|||
api(libs.koin.core)
|
||||
implementation(libs.koin.compose)
|
||||
implementation(libs.koin.compose.viewmodel)
|
||||
// composable component.
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.network)
|
||||
// common
|
||||
implementation(projects.shared)
|
||||
// network
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:pathData="M0 0h24v24H0z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:pathData="M0 0h24v24H0z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41 0.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:pathData="M0 0h24v24H0V0z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M21 3L3 10.53v0.98l6.84 2.65L12.48 21h0.98L21 3z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M12,2c-5.33,4.55-8,8.48-8,11.8c0,4.98,3.8,8.2,8,8.2s8-3.22,8-8.2C20,10.48,17.33,6.55,12,2z M7.83,14 c0.37,0,0.67,0.26,0.74,0.62c0.41,2.22,2.28,2.98,3.64,2.87c0.43-0.02,0.79,0.32,0.79,0.75c0,0.4-0.32,0.73-0.72,0.75 c-2.13,0.13-4.62-1.09-5.19-4.12C7.01,14.42,7.37,14,7.83,14z" />
|
||||
</vector>
|
||||
|
|
@ -25,16 +25,12 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.min
|
||||
import androidx.compose.ui.window.ApplicationScope
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import com.pixelized.desktop.lwa.composable.key.KeyEventHandler
|
||||
import com.pixelized.desktop.lwa.composable.key.LocalKeyEventHandlers
|
||||
import com.pixelized.desktop.lwa.navigation.screen.MainNavHost
|
||||
import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetDestination
|
||||
import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination
|
||||
import com.pixelized.desktop.lwa.navigation.window.WindowController
|
||||
|
|
@ -45,6 +41,7 @@ import com.pixelized.desktop.lwa.navigation.window.destination.RollHistoryWindow
|
|||
import com.pixelized.desktop.lwa.navigation.window.rememberMaxWindowHeight
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
|
||||
import com.pixelized.desktop.lwa.screen.campaign.CampaignScreen
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetMainNavHost
|
||||
import com.pixelized.desktop.lwa.screen.rollhistory.RollHistoryPage
|
||||
import com.pixelized.desktop.lwa.theme.LwaTheme
|
||||
|
|
@ -55,7 +52,6 @@ import lwacharactersheet.composeapp.generated.resources.network__disconnect__mes
|
|||
import org.jetbrains.compose.resources.getString
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
import org.koin.compose.koinInject
|
||||
import java.awt.Toolkit
|
||||
|
||||
val LocalWindowController = compositionLocalOf<WindowController> {
|
||||
error("Local Window Controller is not yet ready")
|
||||
|
|
@ -87,10 +83,10 @@ fun ApplicationScope.App() {
|
|||
) {
|
||||
Window(
|
||||
onCloseRequest = ::exitApplication,
|
||||
state = rememberWindowState(
|
||||
width = 320.dp + 64.dp,
|
||||
height = 900.dp,
|
||||
),
|
||||
// state = rememberWindowState(
|
||||
// width = 320.dp + 64.dp,
|
||||
// height = 900.dp,
|
||||
// ),
|
||||
title = "LwaCharacterSheet",
|
||||
onKeyEvent = { event ->
|
||||
keyEventHandlers.reversed().any { it(event) }
|
||||
|
|
@ -128,7 +124,8 @@ fun ApplicationScope.App() {
|
|||
}
|
||||
},
|
||||
content = {
|
||||
MainNavHost()
|
||||
// MainNavHost()
|
||||
CampaignScreen()
|
||||
}
|
||||
)
|
||||
NetworkSnackHandler(
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import com.pixelized.desktop.lwa.screen.rollhistory.RollHistoryViewModel
|
|||
import com.pixelized.desktop.lwa.parser.dice.DiceParser
|
||||
import com.pixelized.desktop.lwa.parser.word.WordParser
|
||||
import com.pixelized.desktop.lwa.parser.expression.ExpressionParser
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.detail.CharacterDetailViewModel
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.detail.CharacterDiminishedViewModel
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.ribbon.PlayerRibbonViewModel
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
|
|
@ -86,6 +89,9 @@ val viewModelDependencies
|
|||
viewModelOf(::RollViewModel)
|
||||
viewModelOf(::RollHistoryViewModel)
|
||||
viewModelOf(::NetworkViewModel)
|
||||
viewModelOf(::PlayerRibbonViewModel)
|
||||
viewModelOf(::CharacterDetailViewModel)
|
||||
viewModelOf(::CharacterDiminishedViewModel)
|
||||
}
|
||||
|
||||
val parserDependencies
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ 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
|
||||
import com.pixelized.desktop.lwa.theme.color.LwaColorPalette
|
||||
|
||||
@Stable
|
||||
class BlurContentController(
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ class CharacterSheetJsonFactory(
|
|||
val json = CharacterSheetJsonV1(
|
||||
id = sheet.id,
|
||||
name = sheet.name,
|
||||
thumbnail = sheet.thumbnail,
|
||||
portrait = sheet.portrait,
|
||||
strength = sheet.strength,
|
||||
dexterity = sheet.dexterity,
|
||||
constitution = sheet.constitution,
|
||||
|
|
@ -93,6 +95,8 @@ class CharacterSheetJsonFactory(
|
|||
CharacterSheet(
|
||||
id = json.id,
|
||||
name = json.name,
|
||||
portrait = json.portrait,
|
||||
thumbnail = json.thumbnail,
|
||||
strength = json.strength,
|
||||
dexterity = json.dexterity,
|
||||
constitution = json.constitution,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.pixelized.desktop.lwa.repository.characterSheet.model
|
|||
data class CharacterSheet(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val portrait: String?,
|
||||
val thumbnail: String?,
|
||||
// characteristics
|
||||
val strength: Int,
|
||||
val dexterity: Int,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import kotlinx.serialization.Serializable
|
|||
data class CharacterSheetJsonV1(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val portrait: String?,
|
||||
val thumbnail: String?,
|
||||
// characteristics
|
||||
val strength: Int,
|
||||
val dexterity: Int,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign
|
||||
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.composable.key.KeyHandler
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.detail.CharacterDetail
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.detail.CharacterDetailViewModel
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.detail.CharacterDiminishedViewModel
|
||||
import com.pixelized.desktop.lwa.screen.campaign.player.ribbon.PlayerRibbon
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.DiminishedStatDialog
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun CampaignScreen(
|
||||
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
|
||||
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
|
||||
) {
|
||||
KeyHandler {
|
||||
when {
|
||||
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
|
||||
characterDetailViewModel.hideCharacter()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
Surface {
|
||||
CampaignScreenLayout(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
top = {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.height(32.dp)
|
||||
.fillMaxWidth(),
|
||||
elevation = 1.dp,
|
||||
) {
|
||||
|
||||
}
|
||||
},
|
||||
bottom = {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.height(48.dp)
|
||||
.fillMaxWidth(),
|
||||
elevation = 1.dp,
|
||||
) {
|
||||
|
||||
}
|
||||
},
|
||||
main = {
|
||||
|
||||
},
|
||||
leftOverlay = {
|
||||
PlayerRibbon(
|
||||
modifier = Modifier
|
||||
.padding(all = 8.dp)
|
||||
.fillMaxHeight(),
|
||||
onCharacter = {
|
||||
characterDetailViewModel.showCharacter(id = it)
|
||||
},
|
||||
)
|
||||
},
|
||||
rightOverlay = {
|
||||
CharacterDetail(
|
||||
modifier = Modifier
|
||||
.padding(all = 8.dp)
|
||||
.fillMaxHeight(),
|
||||
viewModel = characterDetailViewModel,
|
||||
dismissedViewModel = dismissedViewModel,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
DiminishedStatDialog(
|
||||
dialog = dismissedViewModel.diminishedDialog,
|
||||
onConfirm = { diminished ->
|
||||
dismissedViewModel.changeDiminished(
|
||||
dialog = diminished
|
||||
)
|
||||
dismissedViewModel.hideDiminishedDialog()
|
||||
},
|
||||
onDismissRequest = {
|
||||
dismissedViewModel.hideDiminishedDialog()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CampaignScreenLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
top: @Composable CampaignScreenScope.() -> Unit,
|
||||
bottom: @Composable CampaignScreenScope.() -> Unit,
|
||||
main: @Composable CampaignScreenScope.() -> Unit,
|
||||
leftOverlay: @Composable CampaignScreenScope.() -> Unit,
|
||||
rightOverlay: @Composable CampaignScreenScope.() -> Unit,
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
val leftOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
||||
val rightOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
||||
val scope = remember {
|
||||
CampaignScreenScope(
|
||||
leftOverlay = leftOverlayState,
|
||||
rightOverlay = rightOverlayState,
|
||||
)
|
||||
}
|
||||
with(scope) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
top()
|
||||
Box(
|
||||
modifier = Modifier.weight(1f, fill = true),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.Center)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
main()
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterStart)
|
||||
.onSizeChanged { leftOverlayState.value = it.toDp(density) },
|
||||
) {
|
||||
leftOverlay()
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterEnd)
|
||||
.onSizeChanged { rightOverlayState.value = it.toDp(density) },
|
||||
) {
|
||||
rightOverlay()
|
||||
}
|
||||
}
|
||||
bottom()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CampaignScreenScope(
|
||||
val leftOverlay: State<DpSize>,
|
||||
val rightOverlay: State<DpSize>,
|
||||
)
|
||||
|
||||
private fun IntSize.toDp(density: Density) = with(density) {
|
||||
DpSize(
|
||||
width = width.toDp(),
|
||||
height = height.toDp(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign.player.detail
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.togetherWith
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.DiminishedStatDialog
|
||||
import com.pixelized.desktop.lwa.theme.lwa
|
||||
import kotlinx.coroutines.launch
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_near_me
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_skull_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Stable
|
||||
data class CharacterDetailUio(
|
||||
val id: String,
|
||||
val portrait: String?,
|
||||
val name: String,
|
||||
val hp: String,
|
||||
val pp: String,
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class CharacterDynDetailUio(
|
||||
val hp: String,
|
||||
val pp: String,
|
||||
val mov: String,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CharacterDetail(
|
||||
modifier: Modifier = Modifier,
|
||||
dismissedViewModel: CharacterDiminishedViewModel,
|
||||
viewModel: CharacterDetailViewModel = koinViewModel(),
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val detail = viewModel.detail.collectAsState()
|
||||
|
||||
AnimatedContent(
|
||||
modifier = modifier,
|
||||
targetState = detail.value,
|
||||
transitionSpec = {
|
||||
(fadeIn() + slideInHorizontally { it / 2 }).togetherWith(fadeOut())
|
||||
}
|
||||
) {
|
||||
when (it) {
|
||||
null -> Box(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
)
|
||||
|
||||
else -> {
|
||||
val dynDetail = viewModel.collectDynamicDetailAsState(id = it.id)
|
||||
|
||||
CharacterDetailContent(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(width = 128.dp * 4),
|
||||
character = it,
|
||||
dynDetail = dynDetail,
|
||||
onDismissRequest = { viewModel.hideCharacter() },
|
||||
onDiminished = {
|
||||
scope.launch {
|
||||
dismissedViewModel.showDiminishedDialog(id = it.id)
|
||||
}
|
||||
},
|
||||
onHp = { },
|
||||
onPp = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CharacterDetailContent(
|
||||
modifier: Modifier = Modifier,
|
||||
character: CharacterDetailUio,
|
||||
dynDetail: State<CharacterDynDetailUio>,
|
||||
onDismissRequest: () -> Unit,
|
||||
onDiminished: () -> Unit,
|
||||
onHp: () -> Unit,
|
||||
onPp: () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Background(
|
||||
character = character,
|
||||
)
|
||||
Column {
|
||||
CharacterHeader(
|
||||
modifier = Modifier.padding(start = 16.dp).fillMaxWidth(),
|
||||
character = character,
|
||||
onDismissRequest = onDismissRequest,
|
||||
dynDetail = dynDetail,
|
||||
onDiminished = onDiminished,
|
||||
onHp = onHp,
|
||||
onPp = onPp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Background(
|
||||
modifier: Modifier = Modifier,
|
||||
character: CharacterDetailUio,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
color = MaterialTheme.lwa.color.elevatedSurface,
|
||||
) {
|
||||
// Image(
|
||||
// modifier = Modifier.fillMaxSize().drawWithContent {
|
||||
// drawContent()
|
||||
// drawRect(
|
||||
// brush = Brush.verticalGradient(
|
||||
// listOfNotNull(
|
||||
// color?.copy(alpha = 0.7f),
|
||||
// color,
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// },
|
||||
// painter = rememberAsyncImagePainter(model = character.portrait),
|
||||
// contentDescription = null,
|
||||
// contentScale = ContentScale.Crop,
|
||||
// alignment = Alignment.TopCenter,
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CharacterHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
character: CharacterDetailUio,
|
||||
dynDetail: State<CharacterDynDetailUio>,
|
||||
onDismissRequest: () -> Unit,
|
||||
onDiminished: () -> Unit,
|
||||
onHp: () -> Unit,
|
||||
onPp: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f)
|
||||
.align(alignment = Alignment.CenterVertically),
|
||||
style = MaterialTheme.typography.h5,
|
||||
text = character.name,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
IconButton(
|
||||
onClick = onDiminished,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size = 24.dp),
|
||||
painter = painterResource(Res.drawable.ic_skull_24dp),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = onDismissRequest,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(Res.drawable.ic_close_24dp),
|
||||
tint = MaterialTheme.lwa.color.base.primary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.clip(shape = CircleShape).clickable { onHp() },
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||
painter = painterResource(Res.drawable.ic_heart_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.lwa.color.base.primary,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = dynDetail.value.hp,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
fontWeight = FontWeight.Thin,
|
||||
text = "/${character.hp}",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.clip(shape = CircleShape).clickable { onPp() },
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.lwa.color.base.primary,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = dynDetail.value.pp,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
fontWeight = FontWeight.Thin,
|
||||
text = "/${character.pp}",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(bottom = 4.dp, end = 2.dp).size(12.dp),
|
||||
painter = painterResource(Res.drawable.ic_near_me),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
text = dynDetail.value.mov,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = "m",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign.player.detail
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class CharacterDetailViewModel(
|
||||
private val repository: CharacterSheetRepository,
|
||||
private val alteration: AlterationRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private val displayedCharacterId = MutableStateFlow<String?>(null)
|
||||
|
||||
val detail: StateFlow<CharacterDetailUio?> = combine(
|
||||
displayedCharacterId,
|
||||
repository.characterSheetFlow(),
|
||||
) { id, sheets ->
|
||||
val sheet = sheets.firstOrNull { it.id == id }
|
||||
if (sheet == null) return@combine null
|
||||
CharacterDetailUio(
|
||||
id = sheet.id,
|
||||
portrait = sheet.portrait,
|
||||
name = sheet.name,
|
||||
hp = "${sheet.maxHp}",
|
||||
pp = "${sheet.maxPp}",
|
||||
)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = null,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@Stable
|
||||
fun collectDynamicDetailAsState(id: String): State<CharacterDynDetailUio> {
|
||||
val flow = remember(id) {
|
||||
repository.characterSheetFlow(id = id)
|
||||
}
|
||||
return remember(id) {
|
||||
flow.mapNotNull { sheet ->
|
||||
if (sheet == null) return@mapNotNull null
|
||||
CharacterDynDetailUio(
|
||||
hp = sheet.currentHp.toString(),
|
||||
pp = sheet.currentPp.toString(),
|
||||
mov = sheet.movement.toString(),
|
||||
)
|
||||
}
|
||||
}.collectAsState(
|
||||
initial = CharacterDynDetailUio(
|
||||
hp = flow.value?.maxHp?.toString() ?: "",
|
||||
pp = flow.value?.maxPp?.toString() ?: "",
|
||||
mov = flow.value?.movement?.toString() ?: "",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun showCharacter(id: String) {
|
||||
displayedCharacterId.value = id
|
||||
}
|
||||
|
||||
fun hideCharacter() {
|
||||
displayedCharacterId.value = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign.player.detail
|
||||
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog.DiminishedStatDialogUio
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet__diminished__label
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
||||
class CharacterDiminishedViewModel(
|
||||
private val repository: CharacterSheetRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _diminishedDialog = mutableStateOf<DiminishedStatDialogUio?>(null)
|
||||
val diminishedDialog: State<DiminishedStatDialogUio?> get() = _diminishedDialog
|
||||
|
||||
suspend fun showDiminishedDialog(id: String) {
|
||||
val diminished = repository.characterDiminishedFlow(id = id).value
|
||||
val textFieldValue =
|
||||
mutableStateOf(TextFieldValue("$diminished", selection = TextRange(index = 0)))
|
||||
_diminishedDialog.value = DiminishedStatDialogUio(
|
||||
id = id,
|
||||
label = getString(resource = Res.string.character_sheet__diminished__label),
|
||||
value = { textFieldValue.value },
|
||||
onValueChange = { value ->
|
||||
textFieldValue.value = when (value.text.toIntOrNull()?.takeIf { it >= 0 }) {
|
||||
null -> TextFieldValue("0", selection = TextRange(index = 0))
|
||||
else -> value
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun hideDiminishedDialog() {
|
||||
_diminishedDialog.value = null
|
||||
}
|
||||
|
||||
fun changeDiminished(dialog: DiminishedStatDialogUio) {
|
||||
val value = dialog.value().text.toIntOrNull() ?: 0
|
||||
repository.setDiminishedForCharacter(
|
||||
id = dialog.id,
|
||||
diminished = value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign.player.ribbon
|
||||
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.layout.ContentScale
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
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 PlayerPortraitUio(
|
||||
val id: String,
|
||||
val portrait: String?,
|
||||
val hp: Int,
|
||||
val maxHp: Int,
|
||||
val pp: Int,
|
||||
val maxPp: Int,
|
||||
)
|
||||
|
||||
object PlayerPortrait {
|
||||
object Default {
|
||||
val size = DpSize(96.dp, 128.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlayerPortrait(
|
||||
modifier: Modifier = Modifier,
|
||||
size: DpSize = PlayerPortrait.Default.size,
|
||||
character: PlayerPortraitUio,
|
||||
onCharacter: (id: String) -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(size = size)
|
||||
.clip(shape = remember { RoundedCornerShape(8.dp) })
|
||||
.clickable { onCharacter(character.id) },
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
model = character.portrait,
|
||||
contentScale = ContentScale.Crop,
|
||||
alignment = Alignment.TopCenter,
|
||||
filterQuality = FilterQuality.High,
|
||||
contentDescription = null,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.drawWithContent {
|
||||
drawRect(
|
||||
brush = Brush.verticalGradient(
|
||||
listOf(
|
||||
Color.Black.copy(alpha = 0.0f),
|
||||
Color.Black.copy(alpha = 0.0f),
|
||||
Color.Black.copy(alpha = 0.0f),
|
||||
Color.Black.copy(alpha = 0.5f),
|
||||
Color.Black.copy(alpha = 0.8f),
|
||||
)
|
||||
)
|
||||
)
|
||||
drawContent()
|
||||
}
|
||||
.padding(all = 2.dp),
|
||||
verticalArrangement = Arrangement.aligned(alignment = Alignment.Bottom),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 2.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(12.dp),
|
||||
painter = painterResource(Res.drawable.ic_heart_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 2.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = "${character.hp}/${character.maxHp}",
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 2.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(12.dp),
|
||||
painter = painterResource(Res.drawable.ic_water_drop_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 2.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = "${character.pp}/${character.maxPp}",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign.player.ribbon
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun PlayerRibbon(
|
||||
modifier: Modifier = Modifier,
|
||||
playerRibbonViewModel: PlayerRibbonViewModel = koinViewModel(),
|
||||
onCharacter: (id: String) -> Unit,
|
||||
) {
|
||||
val characters = playerRibbonViewModel.characters.collectAsState()
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||
) {
|
||||
items(
|
||||
items = characters.value,
|
||||
key = { it.id },
|
||||
) {
|
||||
PlayerPortrait(
|
||||
character = it,
|
||||
onCharacter = onCharacter,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.pixelized.desktop.lwa.screen.campaign.player.ribbon
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class PlayerRibbonViewModel(
|
||||
repository: CharacterSheetRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
val characters = repository.characterSheetFlow()
|
||||
.map { sheets ->
|
||||
sheets.map { sheet ->
|
||||
PlayerPortraitUio(
|
||||
id = sheet.id,
|
||||
portrait = sheet.thumbnail,
|
||||
hp = sheet.currentHp,
|
||||
maxHp = sheet.maxHp,
|
||||
pp = sheet.currentPp,
|
||||
maxPp = sheet.maxPp,
|
||||
)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyList()
|
||||
)
|
||||
}
|
||||
|
|
@ -197,6 +197,7 @@ class CharacterSheetViewModel(
|
|||
val textFieldValue =
|
||||
mutableStateOf(TextFieldValue("$diminished", selection = TextRange(index = 0)))
|
||||
_diminishedDialog.value = DiminishedStatDialogUio(
|
||||
id = argument.id,
|
||||
label = getString(resource = Res.string.character_sheet__diminished__label),
|
||||
value = { textFieldValue.value },
|
||||
onValueChange = { value ->
|
||||
|
|
@ -215,7 +216,7 @@ class CharacterSheetViewModel(
|
|||
fun changeDiminished(dialog: DiminishedStatDialogUio) {
|
||||
val value = dialog.value().text.toIntOrNull() ?: 0
|
||||
repository.setDiminishedForCharacter(
|
||||
id = argument.id,
|
||||
id = dialog.id,
|
||||
diminished = value,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import org.jetbrains.compose.resources.stringResource
|
|||
|
||||
@Stable
|
||||
data class DiminishedStatDialogUio(
|
||||
val id: String,
|
||||
val label: String,
|
||||
val value: () -> TextFieldValue,
|
||||
val onValueChange: (TextFieldValue) -> Unit,
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ class CharacterSheetEditFactory(
|
|||
return CharacterSheet(
|
||||
id = editedSheet.id,
|
||||
name = editedSheet.name.value.value,
|
||||
portrait = currentSheet?.portrait,
|
||||
thumbnail = currentSheet?.thumbnail,
|
||||
strength = editedSheet.strength.unpack()?.toIntOrNull()
|
||||
?: currentSheet?.strength
|
||||
?: 0,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,47 @@
|
|||
package com.pixelized.desktop.lwa.theme
|
||||
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
import com.pixelized.desktop.lwa.theme.color.LwaColorTheme
|
||||
import com.pixelized.desktop.lwa.theme.color.darkLwaColorTheme
|
||||
|
||||
val LocalLwaTheme = compositionLocalOf<LwaTheme> {
|
||||
error("Local Snack Controller is not yet ready")
|
||||
}
|
||||
|
||||
val MaterialTheme.lwa: LwaTheme
|
||||
@Composable
|
||||
@Stable
|
||||
get() = LocalLwaTheme.current
|
||||
|
||||
@Stable
|
||||
data class LwaTheme(
|
||||
val color: LwaColorTheme,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun LwaTheme(
|
||||
content: @Composable () -> Unit
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MaterialTheme(
|
||||
colors = darkColors(),
|
||||
typography = MaterialTheme.typography,
|
||||
shapes = MaterialTheme.shapes,
|
||||
content = content,
|
||||
)
|
||||
val lwaColorTheme = darkLwaColorTheme()
|
||||
val theme = remember {
|
||||
LwaTheme(
|
||||
color = lwaColorTheme,
|
||||
)
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalLwaTheme provides theme
|
||||
) {
|
||||
MaterialTheme(
|
||||
colors = lwaColorTheme.base,
|
||||
typography = MaterialTheme.typography,
|
||||
shapes = MaterialTheme.shapes,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.desktop.lwa.theme
|
||||
package com.pixelized.desktop.lwa.theme.color
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.pixelized.desktop.lwa.theme.color
|
||||
|
||||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.ln
|
||||
|
||||
@Stable
|
||||
data class LwaColorTheme(
|
||||
val base: Colors,
|
||||
val elevatedSurface: Color,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@Stable
|
||||
fun darkLwaColorTheme(
|
||||
base: Colors = darkColors(),
|
||||
elevatedSurface: Color = base.calculateElevatedColor(
|
||||
color = base.surface,
|
||||
onColor = base.onSurface,
|
||||
elevation = 1.dp,
|
||||
),
|
||||
): LwaColorTheme = LwaColorTheme(
|
||||
base = base,
|
||||
elevatedSurface = elevatedSurface,
|
||||
)
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
private fun Colors.calculateElevatedColor(color: Color, onColor: Color, elevation: Dp): Color {
|
||||
return if (elevation > 0.dp && !isLight) {
|
||||
val foregroundColor = calculateForegroundColor(onColor, elevation)
|
||||
foregroundColor.compositeOver(color)
|
||||
} else {
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
private fun calculateForegroundColor(color: Color, elevation: Dp): Color {
|
||||
val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
|
||||
return color.copy(alpha = alpha)
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ ktor = "3.0.1"
|
|||
koin = "4.0.0"
|
||||
turtle = "0.5.0"
|
||||
logback = "1.5.11"
|
||||
|
||||
coil = "3.1.0"
|
||||
|
||||
[plugins]
|
||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
||||
|
|
@ -42,4 +42,7 @@ ktor-server-netty = { group = 'io.ktor', name = "ktor-server-netty", version.ref
|
|||
ktor-server-websockets = { group = 'io.ktor', name = "ktor-server-websockets", version.ref = "ktor" }
|
||||
|
||||
turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" }
|
||||
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
coil-network = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||
Loading…
Add table
Add a link
Reference in a new issue