New Inventory component.
This commit is contained in:
		
							parent
							
								
									250d9cbb7c
								
							
						
					
					
						commit
						531e4bea98
					
				
					 25 changed files with 842 additions and 77 deletions
				
			
		| 
						 | 
				
			
			@ -38,7 +38,7 @@ import com.pixelized.rplexicon.ui.screens.rolls.RollOverlay
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.rolls.RollOverlayViewModel
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.rolls.rememberBlurredRollOverlayHostState
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import dagger.hilt.android.AndroidEntryPoint
 | 
			
		||||
 | 
			
		||||
val NO_WINDOW_INSETS = WindowInsets(0, 0, 0, 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +128,7 @@ class MainActivity : ComponentActivity() {
 | 
			
		|||
                                Snackbar(
 | 
			
		||||
                                    modifier = Modifier
 | 
			
		||||
                                        .padding(horizontal = 16.dp)
 | 
			
		||||
                                        .ddBorder(
 | 
			
		||||
                                        .doubleBorder(
 | 
			
		||||
                                            inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                                            outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                                        ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
 | 
			
		|||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
			
		||||
import androidx.compose.foundation.ExperimentalFoundationApi
 | 
			
		||||
import androidx.compose.foundation.background
 | 
			
		||||
import androidx.compose.foundation.isSystemInDarkTheme
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +34,7 @@ import androidx.compose.ui.window.DialogProperties
 | 
			
		|||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.toLabel
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +65,7 @@ fun HandleHitPointEditDialog(
 | 
			
		|||
            onDismissRequest = onDismissRequest,
 | 
			
		||||
        ) {
 | 
			
		||||
            Surface(
 | 
			
		||||
                modifier = Modifier.ddBorder(
 | 
			
		||||
                modifier = Modifier.doubleBorder(
 | 
			
		||||
                    inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                    outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp
 | 
			
		|||
import androidx.compose.ui.window.Dialog
 | 
			
		||||
import androidx.compose.ui.window.DialogProperties
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class SkillEditDialogUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ fun HandleSkillEditDialog(
 | 
			
		|||
            onDismissRequest = onDismissRequest,
 | 
			
		||||
        ) {
 | 
			
		||||
            Surface(
 | 
			
		||||
                modifier = Modifier.ddBorder(
 | 
			
		||||
                modifier = Modifier.doubleBorder(
 | 
			
		||||
                    inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                    outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ import androidx.compose.ui.window.Dialog
 | 
			
		|||
import androidx.compose.ui.window.DialogProperties
 | 
			
		||||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class SpellEditDialogUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ fun HandleSpellEditDialog(
 | 
			
		|||
            onDismissRequest = onDismissRequest,
 | 
			
		||||
        ) {
 | 
			
		||||
            Surface(
 | 
			
		||||
                modifier = Modifier.ddBorder(
 | 
			
		||||
                modifier = Modifier.doubleBorder(
 | 
			
		||||
                    inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                    outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ package com.pixelized.rplexicon.ui.screens.character.composable.actions
 | 
			
		|||
 | 
			
		||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
 | 
			
		||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.annotation.DrawableRes
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
| 
						 | 
				
			
			@ -27,14 +26,12 @@ import androidx.compose.ui.tooling.preview.Preview
 | 
			
		|||
import androidx.compose.ui.tooling.preview.PreviewParameter
 | 
			
		||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.core.net.toUri
 | 
			
		||||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.data.model.Attack
 | 
			
		||||
import com.pixelized.rplexicon.ui.composable.images.AsyncImage
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.character.composable.common.DiceButton
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.character.composable.common.FlatValue
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.uri
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class AttackUio(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.sizeIn
 | 
			
		|||
import androidx.compose.foundation.layout.width
 | 
			
		||||
import androidx.compose.foundation.shape.CutCornerShape
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material3.Divider
 | 
			
		||||
import androidx.compose.material3.HorizontalDivider
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Surface
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +25,7 @@ import androidx.compose.ui.tooling.preview.Preview
 | 
			
		|||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.toLabel
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +87,7 @@ private fun StatPreview() {
 | 
			
		|||
            Stat(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .padding(all = 8.dp)
 | 
			
		||||
                    .ddBorder(
 | 
			
		||||
                    .doubleBorder(
 | 
			
		||||
                        inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                        outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                    ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ package com.pixelized.rplexicon.ui.screens.character.composable.dialogs
 | 
			
		|||
 | 
			
		||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
 | 
			
		||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.compose.foundation.ScrollState
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.interaction.MutableInteractionSource
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +36,7 @@ import com.pixelized.rplexicon.ui.composable.images.BackgroundImage
 | 
			
		|||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class AlterationDialogDetailUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +74,7 @@ fun AlterationDetailDialog(
 | 
			
		|||
                            enabled = false,
 | 
			
		||||
                            onClick = { },
 | 
			
		||||
                        )
 | 
			
		||||
                        .ddBorder(
 | 
			
		||||
                        .doubleBorder(
 | 
			
		||||
                            inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                            outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                        ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ import com.pixelized.rplexicon.ui.composable.images.BackgroundImage
 | 
			
		|||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class SkillDialogDetailUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ fun SkillDetailDialog(
 | 
			
		|||
                            enabled = false,
 | 
			
		||||
                            onClick = { },
 | 
			
		||||
                        )
 | 
			
		||||
                        .ddBorder(
 | 
			
		||||
                        .doubleBorder(
 | 
			
		||||
                            inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                            outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                        ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
package com.pixelized.rplexicon.ui.screens.character.composable.dialogs
 | 
			
		||||
 | 
			
		||||
import android.content.res.Configuration
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import androidx.compose.foundation.ScrollState
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +39,7 @@ import com.pixelized.rplexicon.ui.composable.images.BackgroundImage
 | 
			
		|||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class SpellDialogDetailUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +82,7 @@ fun SpellDetailDialog(
 | 
			
		|||
                            enabled = false,
 | 
			
		||||
                            onClick = { },
 | 
			
		||||
                        )
 | 
			
		||||
                        .ddBorder(
 | 
			
		||||
                        .doubleBorder(
 | 
			
		||||
                            inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                            outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
                        ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,176 @@
 | 
			
		|||
package com.pixelized.rplexicon.ui.screens.character.pages.inventory
 | 
			
		||||
 | 
			
		||||
import android.content.res.Configuration
 | 
			
		||||
import androidx.compose.animation.AnimatedContent
 | 
			
		||||
import androidx.compose.animation.SizeTransform
 | 
			
		||||
import androidx.compose.animation.fadeIn
 | 
			
		||||
import androidx.compose.animation.fadeOut
 | 
			
		||||
import androidx.compose.animation.slideInVertically
 | 
			
		||||
import androidx.compose.animation.slideOutVertically
 | 
			
		||||
import androidx.compose.animation.togetherWith
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.aspectRatio
 | 
			
		||||
import androidx.compose.foundation.layout.offset
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Surface
 | 
			
		||||
import androidx.compose.material3.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.graphics.graphicsLayer
 | 
			
		||||
import androidx.compose.ui.layout.ContentScale
 | 
			
		||||
import androidx.compose.ui.text.font.FontWeight
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.text.style.TextOverflow
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.tooling.preview.PreviewParameter
 | 
			
		||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.ui.composable.images.AsyncImage
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class InventoryItemUio(
 | 
			
		||||
    val id: Int,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val amount: Int,
 | 
			
		||||
    val container: Boolean,
 | 
			
		||||
    val icon: Any?,
 | 
			
		||||
    val items: List<InventoryItemUio> = emptyList(),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun InventoryItem(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    item: InventoryItemUio,
 | 
			
		||||
) {
 | 
			
		||||
    Box(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .then(other = modifier)
 | 
			
		||||
            .aspectRatio(ratio = 1f),
 | 
			
		||||
    ) {
 | 
			
		||||
        AsyncImage(
 | 
			
		||||
            modifier = Modifier.matchParentSize(),
 | 
			
		||||
            model = item.icon,
 | 
			
		||||
            contentScale = ContentScale.Fit,
 | 
			
		||||
        )
 | 
			
		||||
        AnimatedContent(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .align(alignment = Alignment.BottomEnd)
 | 
			
		||||
                .offset(x = 0.dp, y = 4.dp)
 | 
			
		||||
                .padding(horizontal = 2.dp),
 | 
			
		||||
            targetState = item.amount,
 | 
			
		||||
            transitionSpec = {
 | 
			
		||||
                // Compare the incoming number with the previous number.
 | 
			
		||||
                if (targetState > initialState) {
 | 
			
		||||
                    // If the target number is larger, it slides up and fades in
 | 
			
		||||
                    // while the initial (smaller) number slides up and fades out.
 | 
			
		||||
                    slideInVertically { height -> height / 2 } + fadeIn() togetherWith
 | 
			
		||||
                            slideOutVertically { height -> -height / 2 } + fadeOut()
 | 
			
		||||
                } else {
 | 
			
		||||
                    // If the target number is smaller, it slides down and fades in
 | 
			
		||||
                    // while the initial number slides down and fades out.
 | 
			
		||||
                    slideInVertically { height -> -height / 2 } + fadeIn() togetherWith
 | 
			
		||||
                            slideOutVertically { height -> height / 2 } + fadeOut()
 | 
			
		||||
                }.using(
 | 
			
		||||
                    // Disable clipping since the faded slide-in/out should
 | 
			
		||||
                    // be displayed out of bounds.
 | 
			
		||||
                    SizeTransform(clip = false)
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            label = "Container count size",
 | 
			
		||||
        ) { amount ->
 | 
			
		||||
            Text(
 | 
			
		||||
                modifier = Modifier.graphicsLayer { this.alpha = if (amount == 1) 0f else 1f },
 | 
			
		||||
                maxLines = 1,
 | 
			
		||||
                overflow = TextOverflow.Ellipsis,
 | 
			
		||||
                style = MaterialTheme.typography.bodyLarge,
 | 
			
		||||
                fontWeight = FontWeight.Bold,
 | 
			
		||||
                textAlign = TextAlign.End,
 | 
			
		||||
                text = amount.takeIf { it > 0 }?.let { "$it" } ?: " ", //  
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        AnimatedContent(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .align(alignment = Alignment.TopEnd)
 | 
			
		||||
                .offset(x = 0.dp, y = (-4).dp)
 | 
			
		||||
                .padding(horizontal = 2.dp),
 | 
			
		||||
            targetState = item.items.size,
 | 
			
		||||
            transitionSpec = {
 | 
			
		||||
                // Compare the incoming number with the previous number.
 | 
			
		||||
                if (targetState > initialState) {
 | 
			
		||||
                    // If the target number is larger, it slides up and fades in
 | 
			
		||||
                    // while the initial (smaller) number slides up and fades out.
 | 
			
		||||
                    slideInVertically { height -> height / 2 } + fadeIn() togetherWith
 | 
			
		||||
                            slideOutVertically { height -> -height / 2 } + fadeOut()
 | 
			
		||||
                } else {
 | 
			
		||||
                    // If the target number is smaller, it slides down and fades in
 | 
			
		||||
                    // while the initial number slides down and fades out.
 | 
			
		||||
                    slideInVertically { height -> -height / 2 } + fadeIn() togetherWith
 | 
			
		||||
                            slideOutVertically { height -> height / 2 } + fadeOut()
 | 
			
		||||
                }.using(
 | 
			
		||||
                    // Disable clipping since the faded slide-in/out should
 | 
			
		||||
                    // be displayed out of bounds.
 | 
			
		||||
                    SizeTransform(clip = false)
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            label = "Container count size",
 | 
			
		||||
        ) { amount ->
 | 
			
		||||
            Text(
 | 
			
		||||
                maxLines = 1,
 | 
			
		||||
                overflow = TextOverflow.Ellipsis,
 | 
			
		||||
                style = MaterialTheme.typography.bodyLarge,
 | 
			
		||||
                fontWeight = FontWeight.Bold,
 | 
			
		||||
                textAlign = TextAlign.End,
 | 
			
		||||
                text = amount.takeIf { it > 0 }?.let { "$it" } ?: " ", //  
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, widthDp = 64)
 | 
			
		||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 64)
 | 
			
		||||
private fun InventoryItemPreview(
 | 
			
		||||
    @PreviewParameter(ClassInventoryItemProvider::class) preview: InventoryItemUio,
 | 
			
		||||
) {
 | 
			
		||||
    LexiconTheme {
 | 
			
		||||
        Surface {
 | 
			
		||||
            InventoryItem(
 | 
			
		||||
                item = preview,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private class ClassInventoryItemProvider : PreviewParameterProvider<InventoryItemUio> {
 | 
			
		||||
    override val values: Sequence<InventoryItemUio> = sequenceOf(
 | 
			
		||||
        InventoryItemUio(
 | 
			
		||||
            id = 0,
 | 
			
		||||
            name = "Pouch",
 | 
			
		||||
            amount = 1,
 | 
			
		||||
            container = true,
 | 
			
		||||
            icon = R.drawable.icbg_pouch_a_unfaded,
 | 
			
		||||
 | 
			
		||||
            ),
 | 
			
		||||
        InventoryItemUio(
 | 
			
		||||
            id = 1,
 | 
			
		||||
            name = "Scroll of blessing",
 | 
			
		||||
            amount = 1,
 | 
			
		||||
            container = false,
 | 
			
		||||
            icon = R.drawable.icbg_scroll_of_bless_unfaded,
 | 
			
		||||
 | 
			
		||||
            ),
 | 
			
		||||
        InventoryItemUio(
 | 
			
		||||
            id = 2,
 | 
			
		||||
            name = "Potion of blessing",
 | 
			
		||||
            amount = 2,
 | 
			
		||||
            container = false,
 | 
			
		||||
            icon = R.drawable.icbg_pot_potion_of_healing_unfaded,
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,254 @@
 | 
			
		|||
package com.pixelized.rplexicon.ui.screens.character.pages.inventory
 | 
			
		||||
 | 
			
		||||
import android.content.res.Configuration
 | 
			
		||||
import androidx.compose.animation.animateColor
 | 
			
		||||
import androidx.compose.animation.core.updateTransition
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.GridCells
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Surface
 | 
			
		||||
import androidx.compose.material3.surfaceColorAtElevation
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.mutableIntStateOf
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.compositeOver
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.DraggableItem
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.dragContainer
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.rememberGridDragDropState
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
			
		||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
			
		||||
fun Pouet() {
 | 
			
		||||
    val items = remember {
 | 
			
		||||
        mutableStateOf(
 | 
			
		||||
            listOf(
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 0,
 | 
			
		||||
                    name = "Pouch",
 | 
			
		||||
                    amount = 1,
 | 
			
		||||
                    container = true,
 | 
			
		||||
                    icon = R.drawable.icbg_pouch_a_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 1,
 | 
			
		||||
                    name = "Scroll of blessing",
 | 
			
		||||
                    amount = 1,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_scroll_of_bless_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 2,
 | 
			
		||||
                    name = "Scroll of spirit weapon",
 | 
			
		||||
                    amount = 1,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_book_signedtradebisa_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 3,
 | 
			
		||||
                    name = "Potion of healing",
 | 
			
		||||
                    amount = 2,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_potion_of_superior_healing_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 4,
 | 
			
		||||
                    name = "Potion of supérior healing",
 | 
			
		||||
                    amount = 2,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_pot_potion_of_healing_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 5,
 | 
			
		||||
                    name = "Potion of holy water",
 | 
			
		||||
                    amount = 2,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_grn_holy_water_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 6,
 | 
			
		||||
                    name = "Leather armor",
 | 
			
		||||
                    amount = 1,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_leather_armour_rogue_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 7,
 | 
			
		||||
                    name = "Silver battleaxe",
 | 
			
		||||
                    amount = 0,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_battleaxe_plus_one_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 8,
 | 
			
		||||
                    name = "Hand crossbow",
 | 
			
		||||
                    amount = 0,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_hand_crossbow_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 9,
 | 
			
		||||
                    name = "Goodberry",
 | 
			
		||||
                    amount = 0,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_food_goodberry_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 10,
 | 
			
		||||
                    name = "Goodberry",
 | 
			
		||||
                    amount = 0,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_worg_fang_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 11,
 | 
			
		||||
                    name = "Lantern of revealing",
 | 
			
		||||
                    amount = 0,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_lantern_of_revealing,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 12,
 | 
			
		||||
                    name = "Dust of disappearance",
 | 
			
		||||
                    amount = 0,
 | 
			
		||||
                    container = false,
 | 
			
		||||
                    icon = R.drawable.icbg_haste_spore_grenade_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
                InventoryItemUio(
 | 
			
		||||
                    id = 13,
 | 
			
		||||
                    name = "Pouch",
 | 
			
		||||
                    amount = 1,
 | 
			
		||||
                    container = true,
 | 
			
		||||
                    icon = R.drawable.icbg_pouch_a_unfaded,
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val overIndex = remember { mutableIntStateOf(-1) }
 | 
			
		||||
    val contentPadding = remember { PaddingValues(16.dp) }
 | 
			
		||||
    val gridState = rememberLazyGridState()
 | 
			
		||||
    val dragDropState = rememberGridDragDropState(
 | 
			
		||||
        contentPadding = contentPadding,
 | 
			
		||||
        gridState = gridState,
 | 
			
		||||
        onMove = { fromIndex, toIndex ->
 | 
			
		||||
            items.value = items.value.toMutableList().apply {
 | 
			
		||||
                add(toIndex, removeAt(fromIndex))
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        onOver = { _, toIndex ->
 | 
			
		||||
            val receiver = items.value[toIndex]
 | 
			
		||||
            if (receiver.container) {
 | 
			
		||||
                overIndex.intValue = toIndex
 | 
			
		||||
                false
 | 
			
		||||
            } else {
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        onDrop = { fromIndex, toIndex ->
 | 
			
		||||
            if (fromIndex != toIndex) {
 | 
			
		||||
                val receiver = items.value[toIndex]
 | 
			
		||||
                if (receiver.container) {
 | 
			
		||||
                    items.value = items.value.toMutableList().apply {
 | 
			
		||||
                        val item = removeAt(fromIndex)
 | 
			
		||||
                        val receiverCopy = receiver.copy(
 | 
			
		||||
                            items = receiver.items.toMutableList().also {
 | 
			
		||||
                                it.add(item)
 | 
			
		||||
                            }
 | 
			
		||||
                        )
 | 
			
		||||
                        val receiverIndex = indexOf(receiver)
 | 
			
		||||
                        removeAt(receiverIndex)
 | 
			
		||||
                        add(receiverIndex, receiverCopy)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    LexiconTheme {
 | 
			
		||||
        Surface {
 | 
			
		||||
            LazyVerticalGrid(
 | 
			
		||||
                columns = GridCells.Fixed(5),
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .dragContainer(dragDropState)
 | 
			
		||||
                    .fillMaxSize(),
 | 
			
		||||
                state = gridState,
 | 
			
		||||
                contentPadding = contentPadding,
 | 
			
		||||
                verticalArrangement = Arrangement.spacedBy(8.dp),
 | 
			
		||||
                horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
			
		||||
            ) {
 | 
			
		||||
                itemsIndexed(
 | 
			
		||||
                    items = items.value,
 | 
			
		||||
                    key = { _, item -> item.id },
 | 
			
		||||
                ) { index, item ->
 | 
			
		||||
                    DraggableItem(
 | 
			
		||||
                        dragDropState = dragDropState,
 | 
			
		||||
                        index = index
 | 
			
		||||
                    ) { isDragging, isOvering ->
 | 
			
		||||
                        val colorScheme = MaterialTheme.lexicon.colorScheme
 | 
			
		||||
                        val transition = updateTransition(
 | 
			
		||||
                            targetState = isDragging || isOvering ,
 | 
			
		||||
                            label = "Dragging transition",
 | 
			
		||||
                        )
 | 
			
		||||
                        val backgroundColor = transition.animateColor(
 | 
			
		||||
                            label = "Draggable item background color",
 | 
			
		||||
                        ) {
 | 
			
		||||
                            val surface = colorScheme.base.surfaceColorAtElevation(2.dp)
 | 
			
		||||
                            when (it) {
 | 
			
		||||
                                true -> colorScheme.base.primary.copy(alpha = 0.15f)
 | 
			
		||||
                                    .compositeOver(surface)
 | 
			
		||||
 | 
			
		||||
                                else -> surface
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        val outlineColor = transition.animateColor(
 | 
			
		||||
                            label = "Draggable item outline color",
 | 
			
		||||
                        ) {
 | 
			
		||||
                            when (it) {
 | 
			
		||||
                                true -> colorScheme.base.primary
 | 
			
		||||
                                else -> colorScheme.characterSheet.outlineBorder
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        val innerColor = transition.animateColor(
 | 
			
		||||
                            label = "Draggable item inline color",
 | 
			
		||||
                        ) {
 | 
			
		||||
                            when (it) {
 | 
			
		||||
                                true -> colorScheme.base.primary
 | 
			
		||||
                                else -> colorScheme.characterSheet.innerBorder
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        Box(
 | 
			
		||||
                            modifier = Modifier.doubleBorder(
 | 
			
		||||
                                backgroundColor = backgroundColor.value,
 | 
			
		||||
                                outline = remember { RoundedCornerShape(8.dp) },
 | 
			
		||||
                                outlineColor = outlineColor.value,
 | 
			
		||||
                                inner = remember { RoundedCornerShape(6.dp) },
 | 
			
		||||
                                innerColor = innerColor.value,
 | 
			
		||||
                            )
 | 
			
		||||
                        ) {
 | 
			
		||||
                            InventoryItem(
 | 
			
		||||
                                item = item,
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterSheetStatePreview
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ fun ProficiencyPageContent(
 | 
			
		|||
                Column(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .sizeIn(minWidth = 100.dp, minHeight = 116.dp)
 | 
			
		||||
                        .ddBorder(outline = outline, inner = inner)
 | 
			
		||||
                        .doubleBorder(outline = outline, inner = inner)
 | 
			
		||||
                        .padding(all = 8.dp),
 | 
			
		||||
                    horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
                    verticalArrangement = Arrangement.spacedBy(4.dp),
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +163,7 @@ fun ProficiencyPageContent(
 | 
			
		|||
                }
 | 
			
		||||
                sheet.stats.forEach {
 | 
			
		||||
                    Stat(
 | 
			
		||||
                        modifier = Modifier.ddBorder(inner = inner, outline = outline),
 | 
			
		||||
                        modifier = Modifier.doubleBorder(inner = inner, outline = outline),
 | 
			
		||||
                        stat = it,
 | 
			
		||||
                        onClick = onStats,
 | 
			
		||||
                    )
 | 
			
		||||
| 
						 | 
				
			
			@ -266,28 +266,28 @@ private fun ProficiencyLayout(
 | 
			
		|||
            Column(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .layoutId("SavingThrowsId")
 | 
			
		||||
                    .ddBorder(inner = inner, outline = outline),
 | 
			
		||||
                    .doubleBorder(inner = inner, outline = outline),
 | 
			
		||||
                horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
                content = savingThrows,
 | 
			
		||||
            )
 | 
			
		||||
            Column(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .layoutId("ProficienciesId")
 | 
			
		||||
                    .ddBorder(inner = inner, outline = outline),
 | 
			
		||||
                    .doubleBorder(inner = inner, outline = outline),
 | 
			
		||||
                horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
                content = proficiencies,
 | 
			
		||||
            )
 | 
			
		||||
            Column(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .layoutId("SpeedId")
 | 
			
		||||
                    .ddBorder(inner = inner, outline = outline),
 | 
			
		||||
                    .doubleBorder(inner = inner, outline = outline),
 | 
			
		||||
                content = speed,
 | 
			
		||||
                horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
            )
 | 
			
		||||
            Column(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .layoutId("PassivesId")
 | 
			
		||||
                    .ddBorder(inner = inner, outline = outline),
 | 
			
		||||
                    .doubleBorder(inner = inner, outline = outline),
 | 
			
		||||
                content = passives,
 | 
			
		||||
                horizontalAlignment = Alignment.CenterHorizontally,
 | 
			
		||||
                verticalArrangement = Arrangement.SpaceBetween,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ import com.pixelized.rplexicon.isInDarkTheme
 | 
			
		|||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.annotatedSpan
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.highlightRegex
 | 
			
		||||
import java.util.UUID
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ fun ThrowsCard(
 | 
			
		|||
    Surface(
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
            .fillMaxWidth()
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = inner,
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberAttributesSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class AttributesSummaryUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ fun AttributesSummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberCharacteristicsSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class CharacteristicsSummaryUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ fun CharacteristicsSummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberPassivesSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class PassivesSummaryUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ fun PassivesSummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberProficienciesSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class ProficiencySummaryUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ fun ProficiencySummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberSavingThrowsSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class SavingThrowsSummaryUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ fun SavingThrowsSummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRow
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.common.SummaryRowUio
 | 
			
		||||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberSpellsSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class SpellSummaryUio(
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ fun SpellSummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ import com.pixelized.rplexicon.R
 | 
			
		|||
import com.pixelized.rplexicon.ui.screens.summary.composable.preview.statistic.rememberStatusSummary
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.verticalDivider
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ fun StatusSummary(
 | 
			
		|||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .ddBorder(
 | 
			
		||||
            .doubleBorder(
 | 
			
		||||
                inner = remember { RoundedCornerShape(size = 8.dp) },
 | 
			
		||||
                outline = remember { CutCornerShape(size = 16.dp) },
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ fun lightColorScheme(
 | 
			
		|||
        secondary = BaseColorPalette.Teal80,
 | 
			
		||||
        tertiary = BaseColorPalette.Teal40,
 | 
			
		||||
        onPrimary = Color.White,
 | 
			
		||||
        surfaceTint = BaseColorPalette.Purple40,
 | 
			
		||||
        surfaceTint = Color.Gray,
 | 
			
		||||
    ),
 | 
			
		||||
    placeholder: Color = Color(red = 230, green = 225, blue = 229),
 | 
			
		||||
    sheet: LexiconColors.CharacterSheet = LexiconColors.CharacterSheet(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,287 @@
 | 
			
		|||
package com.pixelized.rplexicon.utilitary
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.core.Animatable
 | 
			
		||||
import androidx.compose.animation.core.Spring
 | 
			
		||||
import androidx.compose.animation.core.VectorConverter
 | 
			
		||||
import androidx.compose.animation.core.VisibilityThreshold
 | 
			
		||||
import androidx.compose.animation.core.spring
 | 
			
		||||
import androidx.compose.foundation.ExperimentalFoundationApi
 | 
			
		||||
import androidx.compose.foundation.gestures.scrollBy
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
 | 
			
		||||
import androidx.compose.foundation.lazy.grid.LazyGridState
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.derivedStateOf
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.rememberCoroutineScope
 | 
			
		||||
import androidx.compose.runtime.rememberUpdatedState
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.geometry.Offset
 | 
			
		||||
import androidx.compose.ui.geometry.Size
 | 
			
		||||
import androidx.compose.ui.graphics.graphicsLayer
 | 
			
		||||
import androidx.compose.ui.input.pointer.pointerInput
 | 
			
		||||
import androidx.compose.ui.platform.LocalDensity
 | 
			
		||||
import androidx.compose.ui.platform.LocalLayoutDirection
 | 
			
		||||
import androidx.compose.ui.unit.IntOffset
 | 
			
		||||
import androidx.compose.ui.unit.IntSize
 | 
			
		||||
import androidx.compose.ui.unit.toOffset
 | 
			
		||||
import androidx.compose.ui.unit.toSize
 | 
			
		||||
import androidx.compose.ui.zIndex
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.lazyGridDragAndDrop.detectDragGesturesAfterLongPress
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.isActive
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
 | 
			
		||||
/***
 | 
			
		||||
 * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyGridDragAndDropDemo.kt
 | 
			
		||||
 */
 | 
			
		||||
@Composable
 | 
			
		||||
fun rememberGridDragDropState(
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    gridState: LazyGridState,
 | 
			
		||||
    onMove: (Int, Int) -> Unit,
 | 
			
		||||
    onOver: (Int, Int) -> Boolean,
 | 
			
		||||
    onDrop: (Int, Int) -> Unit,
 | 
			
		||||
): GridDragDropState {
 | 
			
		||||
    val density = LocalDensity.current
 | 
			
		||||
    val layoutDirection = LocalLayoutDirection.current
 | 
			
		||||
    val scope = rememberCoroutineScope()
 | 
			
		||||
    val currentOnMove = rememberUpdatedState(newValue = onMove)
 | 
			
		||||
    val currentOnOver = rememberUpdatedState(newValue = onOver)
 | 
			
		||||
    val currentOnDrop = rememberUpdatedState(newValue = onDrop)
 | 
			
		||||
 | 
			
		||||
    val state = remember(gridState, scope, currentOnMove) {
 | 
			
		||||
        GridDragDropState(
 | 
			
		||||
            contentOffset = with(density) {
 | 
			
		||||
                Offset(
 | 
			
		||||
                    contentPadding.calculateTopPadding().toPx(),
 | 
			
		||||
                    contentPadding.calculateLeftPadding(layoutDirection).toPx()
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            scope = scope,
 | 
			
		||||
            state = gridState,
 | 
			
		||||
            onMove = currentOnMove.value,
 | 
			
		||||
            onOver = currentOnOver.value,
 | 
			
		||||
            onDrop = currentOnDrop.value,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LaunchedEffect(state) {
 | 
			
		||||
        while (isActive) {
 | 
			
		||||
            val diff = state.scrollChannel.receive()
 | 
			
		||||
            gridState.scrollBy(diff)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class GridDragDropState internal constructor(
 | 
			
		||||
    private val contentOffset: Offset,
 | 
			
		||||
    private val scope: CoroutineScope,
 | 
			
		||||
    private val state: LazyGridState,
 | 
			
		||||
    private val onMove: (Int, Int) -> Unit,
 | 
			
		||||
    private val onOver: (Int, Int) -> Boolean,
 | 
			
		||||
    private val onDrop: (Int, Int) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    internal val scrollChannel = Channel<Float>()
 | 
			
		||||
 | 
			
		||||
    var draggingItemIndex by mutableStateOf<Int?>(null)
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    var overItemIndex by mutableStateOf<Int?>(null)
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
 | 
			
		||||
 | 
			
		||||
    private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
 | 
			
		||||
 | 
			
		||||
    internal val draggingItemOffset: Offset
 | 
			
		||||
        get() = draggingItemLayoutInfo
 | 
			
		||||
            ?.let { item -> draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset() }
 | 
			
		||||
            ?: Offset.Zero
 | 
			
		||||
 | 
			
		||||
    private val draggingItemLayoutInfo: LazyGridItemInfo?
 | 
			
		||||
        get() = state.layoutInfo.visibleItemsInfo
 | 
			
		||||
            .firstOrNull { it.index == draggingItemIndex }
 | 
			
		||||
 | 
			
		||||
    internal var previousIndexOfDraggedItem by mutableStateOf<Int?>(null)
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    internal var previousItemOffset = Animatable(Offset.Zero, Offset.VectorConverter)
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    internal fun onDragStart(offset: Offset) {
 | 
			
		||||
        state.layoutInfo.visibleItemsInfo
 | 
			
		||||
            .firstItemWithOffsetOrNull(offset = offset)
 | 
			
		||||
            ?.let {
 | 
			
		||||
                draggingItemIndex = it.index
 | 
			
		||||
                draggingItemInitialOffset = it.offset.toOffset()
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun onDragInterrupted(offset: Offset) {
 | 
			
		||||
        val localDraggingItemIndex = draggingItemIndex
 | 
			
		||||
        if (localDraggingItemIndex != null) {
 | 
			
		||||
            state.layoutInfo.visibleItemsInfo
 | 
			
		||||
                .firstItemWithOffsetOrNull(offset)
 | 
			
		||||
                ?.let { onDrop(localDraggingItemIndex, it.index) }
 | 
			
		||||
 | 
			
		||||
            previousIndexOfDraggedItem = draggingItemIndex
 | 
			
		||||
            val startOffset = draggingItemOffset
 | 
			
		||||
            scope.launch {
 | 
			
		||||
                previousItemOffset.snapTo(startOffset)
 | 
			
		||||
                previousItemOffset.animateTo(
 | 
			
		||||
                    Offset.Zero,
 | 
			
		||||
                    spring(
 | 
			
		||||
                        stiffness = Spring.StiffnessMediumLow,
 | 
			
		||||
                        visibilityThreshold = Offset.VisibilityThreshold
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
                previousIndexOfDraggedItem = null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        overItemIndex = null
 | 
			
		||||
        draggingItemDraggedDelta = Offset.Zero
 | 
			
		||||
        draggingItemIndex = null
 | 
			
		||||
        draggingItemInitialOffset = Offset.Zero
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun onDrag(offset: Offset) {
 | 
			
		||||
        draggingItemDraggedDelta += offset
 | 
			
		||||
 | 
			
		||||
        val draggingItem = draggingItemLayoutInfo ?: return
 | 
			
		||||
        val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
 | 
			
		||||
        val endOffset = startOffset + draggingItem.size.toSize()
 | 
			
		||||
 | 
			
		||||
        val middleOffset = startOffset + (endOffset - startOffset) / 2f
 | 
			
		||||
        val middleXOffset = middleOffset.x.toInt()
 | 
			
		||||
        val middleYOffset = middleOffset.y.toInt()
 | 
			
		||||
 | 
			
		||||
        val targetItem = state.layoutInfo.visibleItemsInfo.find { item ->
 | 
			
		||||
            draggingItem.index != item.index &&
 | 
			
		||||
                    item.offset.x <= middleXOffset && middleXOffset <= item.offsetEnd.x &&
 | 
			
		||||
                    item.offset.y <= middleYOffset && middleYOffset <= item.offsetEnd.y
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        overItemIndex = targetItem?.index
 | 
			
		||||
 | 
			
		||||
        if (targetItem != null) {
 | 
			
		||||
            if (onOver.invoke(draggingItem.index, targetItem.index)) {
 | 
			
		||||
                onMove.invoke(draggingItem.index, targetItem.index)
 | 
			
		||||
                draggingItemIndex = targetItem.index
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            val overscroll = when {
 | 
			
		||||
                draggingItemDraggedDelta.y > 0 ->
 | 
			
		||||
                    (endOffset.y - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
 | 
			
		||||
 | 
			
		||||
                draggingItemDraggedDelta.y < 0 ->
 | 
			
		||||
                    (startOffset.y - state.layoutInfo.viewportStartOffset).coerceAtMost(0f)
 | 
			
		||||
 | 
			
		||||
                else -> 0f
 | 
			
		||||
            }
 | 
			
		||||
            if (overscroll != 0f) {
 | 
			
		||||
                scrollChannel.trySend(overscroll)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun List<LazyGridItemInfo>.firstItemWithOffsetOrNull(
 | 
			
		||||
        offset: Offset,
 | 
			
		||||
    ): LazyGridItemInfo? {
 | 
			
		||||
        val offsetX = offset.x.toInt() - contentOffset.x.toInt()
 | 
			
		||||
        val offsetY = offset.y.toInt() - contentOffset.y.toInt()
 | 
			
		||||
        return this.firstOrNull { item ->
 | 
			
		||||
            item.offset.x <= offsetX && offsetX <= item.offsetEnd.x &&
 | 
			
		||||
                    item.offset.y <= offsetY && offsetY <= item.offsetEnd.y
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val LazyGridItemInfo.offsetEnd: IntOffset
 | 
			
		||||
        get() = this.offset + this.size
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private operator fun IntOffset.plus(size: IntSize): IntOffset {
 | 
			
		||||
    return IntOffset(x + size.width, y + size.height)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private operator fun Offset.plus(size: Size): Offset {
 | 
			
		||||
    return Offset(x + size.width, y + size.height)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier {
 | 
			
		||||
    return this then Modifier.pointerInput(dragDropState) {
 | 
			
		||||
        detectDragGesturesAfterLongPress(
 | 
			
		||||
            onDrag = { change, offset ->
 | 
			
		||||
                change.consume()
 | 
			
		||||
                dragDropState.onDrag(offset = offset)
 | 
			
		||||
            },
 | 
			
		||||
            onDragStart = { offset ->
 | 
			
		||||
                dragDropState.onDragStart(offset)
 | 
			
		||||
            },
 | 
			
		||||
            onDragEnd = { offset ->
 | 
			
		||||
                dragDropState.onDragInterrupted(offset)
 | 
			
		||||
            },
 | 
			
		||||
            onDragCancel = {
 | 
			
		||||
                dragDropState.onDragInterrupted(Offset.Zero)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@OptIn(ExperimentalFoundationApi::class)
 | 
			
		||||
@Composable
 | 
			
		||||
fun LazyGridItemScope.DraggableItem(
 | 
			
		||||
    dragDropState: GridDragDropState,
 | 
			
		||||
    index: Int,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    content: @Composable (isDragging: Boolean, isOver: Boolean) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    val dragging by remember(index, dragDropState) {
 | 
			
		||||
        derivedStateOf {
 | 
			
		||||
            index == dragDropState.draggingItemIndex
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    val overing by remember(index, dragDropState) {
 | 
			
		||||
        derivedStateOf {
 | 
			
		||||
            index == dragDropState.overItemIndex
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    val draggingModifier = when {
 | 
			
		||||
        dragging -> {
 | 
			
		||||
            Modifier
 | 
			
		||||
                .zIndex(1f)
 | 
			
		||||
                .graphicsLayer {
 | 
			
		||||
                    translationX = dragDropState.draggingItemOffset.x
 | 
			
		||||
                    translationY = dragDropState.draggingItemOffset.y
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        index == dragDropState.previousIndexOfDraggedItem -> {
 | 
			
		||||
            Modifier
 | 
			
		||||
                .zIndex(1f)
 | 
			
		||||
                .graphicsLayer {
 | 
			
		||||
                    translationX = dragDropState.previousItemOffset.value.x
 | 
			
		||||
                    translationY = dragDropState.previousItemOffset.value.y
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else -> {
 | 
			
		||||
            Modifier.animateItemPlacement()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Box(
 | 
			
		||||
        modifier = modifier.then(draggingModifier),
 | 
			
		||||
        propagateMinConstraints = true,
 | 
			
		||||
    ) {
 | 
			
		||||
        content(dragging, overing)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,6 @@ import androidx.compose.material3.MaterialTheme
 | 
			
		|||
import androidx.compose.material3.surfaceColorAtElevation
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.derivedStateOf
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableFloatStateOf
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +105,7 @@ fun Modifier.clickableInterceptor(): Modifier = composed {
 | 
			
		|||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Modifier.ddBorder(
 | 
			
		||||
fun Modifier.doubleBorder(
 | 
			
		||||
    horizontalSpacing: Dp = 3.dp,
 | 
			
		||||
    verticalSpacing: Dp = 3.dp,
 | 
			
		||||
    outline: Shape,
 | 
			
		||||
| 
						 | 
				
			
			@ -114,37 +113,38 @@ fun Modifier.ddBorder(
 | 
			
		|||
    inner: Shape,
 | 
			
		||||
    innerWidth: Dp = 1.dp,
 | 
			
		||||
): Modifier = composed {
 | 
			
		||||
    val isDarkTheme = isInDarkTheme()
 | 
			
		||||
    val elevation = remember { derivedStateOf { if (isDarkTheme) 2.dp else 0.dp } }
 | 
			
		||||
    val colorScheme = MaterialTheme.lexicon.colorScheme
 | 
			
		||||
    this then Modifier
 | 
			
		||||
        .border(
 | 
			
		||||
            width = outlineWidth,
 | 
			
		||||
            color = colorScheme.characterSheet.outlineBorder,
 | 
			
		||||
            shape = outline,
 | 
			
		||||
        )
 | 
			
		||||
        .background(
 | 
			
		||||
            shape = outline,
 | 
			
		||||
            color = colorScheme.base.surfaceColorAtElevation(elevation.value)
 | 
			
		||||
        )
 | 
			
		||||
        .padding(
 | 
			
		||||
            horizontal = horizontalSpacing,
 | 
			
		||||
            vertical = verticalSpacing,
 | 
			
		||||
        )
 | 
			
		||||
        .border(
 | 
			
		||||
            width = innerWidth,
 | 
			
		||||
            color = colorScheme.characterSheet.innerBorder,
 | 
			
		||||
            shape = inner,
 | 
			
		||||
        )
 | 
			
		||||
        .background(
 | 
			
		||||
            shape = inner,
 | 
			
		||||
            color = colorScheme.base.surfaceColorAtElevation(elevation.value)
 | 
			
		||||
        )
 | 
			
		||||
        .clip(
 | 
			
		||||
            shape = inner,
 | 
			
		||||
    this then Modifier.doubleBorder(
 | 
			
		||||
        horizontalSpacing = horizontalSpacing,
 | 
			
		||||
        verticalSpacing = verticalSpacing,
 | 
			
		||||
        backgroundColor = colorScheme.base.surfaceColorAtElevation(1.dp),
 | 
			
		||||
        outline = outline,
 | 
			
		||||
        outlineWidth = outlineWidth,
 | 
			
		||||
        outlineColor = colorScheme.characterSheet.outlineBorder,
 | 
			
		||||
        inner = inner,
 | 
			
		||||
        innerWidth = innerWidth,
 | 
			
		||||
        innerColor = colorScheme.characterSheet.innerBorder,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Modifier.doubleBorder(
 | 
			
		||||
    horizontalSpacing: Dp = 3.dp,
 | 
			
		||||
    verticalSpacing: Dp = 3.dp,
 | 
			
		||||
    backgroundColor: Color,
 | 
			
		||||
    outline: Shape,
 | 
			
		||||
    outlineWidth: Dp = 1.dp,
 | 
			
		||||
    outlineColor: Color,
 | 
			
		||||
    inner: Shape,
 | 
			
		||||
    innerWidth: Dp = 1.dp,
 | 
			
		||||
    innerColor: Color,
 | 
			
		||||
): Modifier = this then Modifier
 | 
			
		||||
    .border(width = outlineWidth, color = outlineColor, shape = outline)
 | 
			
		||||
    .background(shape = outline, color = backgroundColor)
 | 
			
		||||
    .padding(horizontal = horizontalSpacing, vertical = verticalSpacing)
 | 
			
		||||
    .border(width = innerWidth, color = innerColor, shape = inner)
 | 
			
		||||
    .background(shape = inner, color = backgroundColor)
 | 
			
		||||
    .clip(shape = inner)
 | 
			
		||||
 | 
			
		||||
fun Modifier.lexiconShadow(): Modifier {
 | 
			
		||||
    return this then composed {
 | 
			
		||||
        val isDarkTheme = isInDarkTheme()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
package com.pixelized.rplexicon.utilitary.lazyGridDragAndDrop
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.gestures.awaitEachGesture
 | 
			
		||||
import androidx.compose.foundation.gestures.awaitFirstDown
 | 
			
		||||
import androidx.compose.foundation.gestures.awaitLongPressOrCancellation
 | 
			
		||||
import androidx.compose.foundation.gestures.drag
 | 
			
		||||
import androidx.compose.ui.geometry.Offset
 | 
			
		||||
import androidx.compose.ui.input.pointer.PointerInputChange
 | 
			
		||||
import androidx.compose.ui.input.pointer.PointerInputScope
 | 
			
		||||
import androidx.compose.ui.input.pointer.changedToUp
 | 
			
		||||
import androidx.compose.ui.input.pointer.positionChange
 | 
			
		||||
import androidx.compose.ui.util.fastForEach
 | 
			
		||||
import kotlinx.coroutines.CancellationException
 | 
			
		||||
 | 
			
		||||
suspend fun PointerInputScope.detectDragGesturesAfterLongPress(
 | 
			
		||||
    onDragStart: (Offset) -> Unit = { },
 | 
			
		||||
    onDragEnd: (Offset) -> Unit = { },
 | 
			
		||||
    onDragCancel: () -> Unit = { },
 | 
			
		||||
    onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    awaitEachGesture {
 | 
			
		||||
        try {
 | 
			
		||||
            // await for a press
 | 
			
		||||
            val down = awaitFirstDown(requireUnconsumed = false)
 | 
			
		||||
            // await for a long press or a cancellation
 | 
			
		||||
            val drag = awaitLongPressOrCancellation(pointerId = down.id)
 | 
			
		||||
            // if the user did a long press
 | 
			
		||||
            if (drag != null) {
 | 
			
		||||
                // callback onDragStart with the initial position.
 | 
			
		||||
                onDragStart.invoke(drag.position)
 | 
			
		||||
                var lastDragInput: PointerInputChange = drag
 | 
			
		||||
                val dragEnded = drag(
 | 
			
		||||
                    pointerId = drag.id,
 | 
			
		||||
                    onDrag = { input ->
 | 
			
		||||
                        lastDragInput = input
 | 
			
		||||
                        onDrag(input, input.positionChange())
 | 
			
		||||
                        input.consume()
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
                if (dragEnded) {
 | 
			
		||||
                    // consume up if we quit drag gracefully with the up
 | 
			
		||||
                    currentEvent.changes.fastForEach {
 | 
			
		||||
                        if (it.changedToUp()) it.consume()
 | 
			
		||||
                    }
 | 
			
		||||
                    onDragEnd(lastDragInput.position)
 | 
			
		||||
                } else {
 | 
			
		||||
                    onDragCancel()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (c: CancellationException) {
 | 
			
		||||
            onDragCancel()
 | 
			
		||||
            throw c
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_pouch_a_unfaded.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_pouch_a_unfaded.webp
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 12 KiB  | 
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue