Add some sugar on the location feature
This commit is contained in:
		
							parent
							
								
									b6af245327
								
							
						
					
					
						commit
						85cdd69570
					
				
					 2 changed files with 149 additions and 28 deletions
				
			
		| 
						 | 
				
			
			@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier
 | 
			
		|||
import androidx.compose.ui.draw.drawWithContent
 | 
			
		||||
import androidx.compose.ui.geometry.Offset
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.graphics.drawscope.DrawScope
 | 
			
		||||
import androidx.compose.ui.graphics.drawscope.Stroke
 | 
			
		||||
import androidx.compose.ui.graphics.graphicsLayer
 | 
			
		||||
import androidx.compose.ui.input.pointer.pointerInput
 | 
			
		||||
| 
						 | 
				
			
			@ -29,8 +30,14 @@ import androidx.compose.ui.layout.onSizeChanged
 | 
			
		|||
import androidx.compose.ui.unit.IntSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.rplexicon.ui.composable.AsyncImage
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.colors.LexiconColors
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
			
		||||
import com.skydoves.landscapist.ImageOptions
 | 
			
		||||
import kotlin.math.sqrt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private val RADIUS = 12.dp
 | 
			
		||||
private val SQUARE = (RADIUS / sqrt(2f))
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun FantasyMap(
 | 
			
		||||
| 
						 | 
				
			
			@ -39,13 +46,30 @@ fun FantasyMap(
 | 
			
		|||
    imageOptions: ImageOptions = ImageOptions(),
 | 
			
		||||
    @DrawableRes previewPlaceholder: Int,
 | 
			
		||||
    item: State<LocationDetailUio>,
 | 
			
		||||
    highlight: State<Offset>,
 | 
			
		||||
    selectedItem: State<Int>,
 | 
			
		||||
    onMarquee: (MarqueeUio) -> Unit,
 | 
			
		||||
    onTap: (Offset) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val colorScheme = MaterialTheme.lexicon.colorScheme
 | 
			
		||||
    val animatedScale = animateFloatAsState(targetValue = state.scale, label = "ScaleAnimation")
 | 
			
		||||
    val animatedOffset = animateOffsetAsState(targetValue = state.offset, label = "OffsetAnimation")
 | 
			
		||||
 | 
			
		||||
    val animatedScale = animateFloatAsState(
 | 
			
		||||
        targetValue = state.scale,
 | 
			
		||||
        label = "ScaleAnimation",
 | 
			
		||||
    )
 | 
			
		||||
    val animatedOffset = animateOffsetAsState(
 | 
			
		||||
        targetValue = state.offset,
 | 
			
		||||
        label = "OffsetAnimation",
 | 
			
		||||
    )
 | 
			
		||||
    val animatedMarqueeAlpha = animateFloatAsState(
 | 
			
		||||
        targetValue = if (state.freeHand) 0f else 1f,
 | 
			
		||||
        label = "MarqueAlphaAnimation",
 | 
			
		||||
    )
 | 
			
		||||
    val animatedCrossAlpha = remember {
 | 
			
		||||
        derivedStateOf {
 | 
			
		||||
            1f - animatedMarqueeAlpha.value
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LaunchedEffect(key1 = "CenterOnMarquee:${selectedItem.value}") {
 | 
			
		||||
        item.value.marquees.getOrNull(selectedItem.value)?.position
 | 
			
		||||
| 
						 | 
				
			
			@ -95,37 +119,27 @@ fun FantasyMap(
 | 
			
		|||
                .drawWithContent {
 | 
			
		||||
                    drawContent()
 | 
			
		||||
 | 
			
		||||
                    if (state.freeHand.not()) {
 | 
			
		||||
                    if (animatedMarqueeAlpha.value > 0f) {
 | 
			
		||||
                        item.value.marquees.forEachIndexed { index, item ->
 | 
			
		||||
                            if (item.position != Offset.Unspecified) {
 | 
			
		||||
                                drawCircle(
 | 
			
		||||
                                    color = colorScheme.shadow,
 | 
			
		||||
                                    radius = 12.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    style = Stroke(
 | 
			
		||||
                                        width = 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    center = Offset(
 | 
			
		||||
                                        x = size.width * item.position.x,
 | 
			
		||||
                                        y = size.height * item.position.y + 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    )
 | 
			
		||||
                                )
 | 
			
		||||
                                drawCircle(
 | 
			
		||||
                                    color = when (selectedItem.value) {
 | 
			
		||||
                                        index -> colorScheme.base.primary
 | 
			
		||||
                                        else -> Color.White
 | 
			
		||||
                                    },
 | 
			
		||||
                                    radius = 12.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    style = Stroke(
 | 
			
		||||
                                        width = 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    center = Offset(
 | 
			
		||||
                                        x = size.width * item.position.x,
 | 
			
		||||
                                        y = size.height * item.position.y,
 | 
			
		||||
                                    )
 | 
			
		||||
                                drawMarque(
 | 
			
		||||
                                    colorScheme = colorScheme,
 | 
			
		||||
                                    alpha = animatedMarqueeAlpha,
 | 
			
		||||
                                    scale = animatedScale,
 | 
			
		||||
                                    position = item.position,
 | 
			
		||||
                                    selected = selectedItem.value == index
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (highlight.value != Offset.Unspecified && animatedCrossAlpha.value > 0f) {
 | 
			
		||||
                        drawCross(
 | 
			
		||||
                            colorScheme = colorScheme,
 | 
			
		||||
                            alpha = animatedCrossAlpha,
 | 
			
		||||
                            scale = animatedScale,
 | 
			
		||||
                            position = highlight.value,
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .pointerInput("DetectTapGestures") {
 | 
			
		||||
                    detectTapGestures(
 | 
			
		||||
| 
						 | 
				
			
			@ -240,4 +254,100 @@ class FantasyMapState(
 | 
			
		|||
            else -> origin
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun DrawScope.drawMarque(
 | 
			
		||||
    colorScheme: LexiconColors,
 | 
			
		||||
    alpha: State<Float>,
 | 
			
		||||
    scale: State<Float>,
 | 
			
		||||
    position: Offset,
 | 
			
		||||
    selected: Boolean,
 | 
			
		||||
) {
 | 
			
		||||
    drawCircle(
 | 
			
		||||
        color = colorScheme.shadow,
 | 
			
		||||
        alpha = alpha.value,
 | 
			
		||||
        radius = 12.dp.toPx() / scale.value,
 | 
			
		||||
        style = Stroke(
 | 
			
		||||
            width = 2.dp.toPx() / scale.value,
 | 
			
		||||
        ),
 | 
			
		||||
        center = Offset(
 | 
			
		||||
            x = size.width * position.x,
 | 
			
		||||
            y = size.height * position.y + 2.dp.toPx() / scale.value,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    drawCircle(
 | 
			
		||||
        color = when (selected) {
 | 
			
		||||
            true -> colorScheme.base.primary
 | 
			
		||||
            else -> Color.White
 | 
			
		||||
        },
 | 
			
		||||
        alpha = alpha.value,
 | 
			
		||||
        radius = 12.dp.toPx() / scale.value,
 | 
			
		||||
        style = Stroke(
 | 
			
		||||
            width = 2.dp.toPx() / scale.value,
 | 
			
		||||
        ),
 | 
			
		||||
        center = Offset(
 | 
			
		||||
            x = size.width * position.x,
 | 
			
		||||
            y = size.height * position.y,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun DrawScope.drawCross(
 | 
			
		||||
    colorScheme: LexiconColors,
 | 
			
		||||
    alpha: State<Float>,
 | 
			
		||||
    scale: State<Float>,
 | 
			
		||||
    position: Offset,
 | 
			
		||||
) {
 | 
			
		||||
    drawLine(
 | 
			
		||||
        color = colorScheme.shadow,
 | 
			
		||||
        alpha = alpha.value,
 | 
			
		||||
        strokeWidth = 2.dp.toPx() / scale.value,
 | 
			
		||||
        start = Offset(
 | 
			
		||||
            x = size.width * position.x - SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y - SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
 | 
			
		||||
        ),
 | 
			
		||||
        end = Offset(
 | 
			
		||||
            x = size.width * position.x + SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y + SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    drawLine(
 | 
			
		||||
        color = colorScheme.shadow,
 | 
			
		||||
        alpha = alpha.value,
 | 
			
		||||
        strokeWidth = 2.dp.toPx() / scale.value,
 | 
			
		||||
        start = Offset(
 | 
			
		||||
            x = size.width * position.x + SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y - SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
 | 
			
		||||
        ),
 | 
			
		||||
        end = Offset(
 | 
			
		||||
            x = size.width * position.x - SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y + SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    drawLine(
 | 
			
		||||
        color = colorScheme.base.primary,
 | 
			
		||||
        alpha = alpha.value,
 | 
			
		||||
        strokeWidth = 2.dp.toPx() / scale.value,
 | 
			
		||||
        start = Offset(
 | 
			
		||||
            x = size.width * position.x - SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y - SQUARE.toPx() / scale.value,
 | 
			
		||||
        ),
 | 
			
		||||
        end = Offset(
 | 
			
		||||
            x = size.width * position.x + SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y + SQUARE.toPx() / scale.value,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    drawLine(
 | 
			
		||||
        color = colorScheme.base.primary,
 | 
			
		||||
        alpha = alpha.value,
 | 
			
		||||
        strokeWidth = 2.dp.toPx() / scale.value,
 | 
			
		||||
        start = Offset(
 | 
			
		||||
            x = size.width * position.x + SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y - SQUARE.toPx() / scale.value,
 | 
			
		||||
        ),
 | 
			
		||||
        end = Offset(
 | 
			
		||||
            x = size.width * position.x - SQUARE.toPx() / scale.value,
 | 
			
		||||
            y = size.height * position.y + SQUARE.toPx() / scale.value,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,6 @@ import androidx.compose.foundation.verticalScroll
 | 
			
		|||
import androidx.compose.material3.ExperimentalMaterial3Api
 | 
			
		||||
import androidx.compose.material3.FilledIconButton
 | 
			
		||||
import androidx.compose.material3.FilledIconToggleButton
 | 
			
		||||
import androidx.compose.material3.FilledTonalIconButton
 | 
			
		||||
import androidx.compose.material3.Icon
 | 
			
		||||
import androidx.compose.material3.IconButton
 | 
			
		||||
import androidx.compose.material3.IconButtonDefaults
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +107,7 @@ fun LocationDetail(
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val mapHighlight = remember { mutableStateOf(Offset.Unspecified) }
 | 
			
		||||
    val selectedIndex = remember { mutableStateOf(0) }
 | 
			
		||||
 | 
			
		||||
    Surface {
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +119,7 @@ fun LocationDetail(
 | 
			
		|||
            fantasyMapState = fantasy,
 | 
			
		||||
            item = viewModel.location,
 | 
			
		||||
            selectedIndex = selectedIndex,
 | 
			
		||||
            mapHighlight = mapHighlight,
 | 
			
		||||
            onBack = {
 | 
			
		||||
                screen.popBackStack()
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			@ -137,9 +138,16 @@ fun LocationDetail(
 | 
			
		|||
                        actionLabel = ok,
 | 
			
		||||
                        duration = SnackbarDuration.Indefinite,
 | 
			
		||||
                    )
 | 
			
		||||
                    mapHighlight.value = Offset.Unspecified
 | 
			
		||||
                }
 | 
			
		||||
                mapHighlight.value = it
 | 
			
		||||
            },
 | 
			
		||||
            onTouch = {
 | 
			
		||||
                if (it) {
 | 
			
		||||
                    mapHighlight.value = Offset.Unspecified
 | 
			
		||||
                } else {
 | 
			
		||||
                    job.value?.cancel()
 | 
			
		||||
                }
 | 
			
		||||
                fantasy.toggleFreeHand(it)
 | 
			
		||||
            },
 | 
			
		||||
            onCenter = {
 | 
			
		||||
| 
						 | 
				
			
			@ -199,6 +207,7 @@ private fun LocationContent(
 | 
			
		|||
    fantasyMapState: FantasyMapState,
 | 
			
		||||
    item: State<LocationDetailUio>,
 | 
			
		||||
    selectedIndex: State<Int>,
 | 
			
		||||
    mapHighlight: State<Offset>,
 | 
			
		||||
    onBack: () -> Unit,
 | 
			
		||||
    onMarquee: (MarqueeUio) -> Unit,
 | 
			
		||||
    onMapTap: (Offset) -> Unit,
 | 
			
		||||
| 
						 | 
				
			
			@ -266,6 +275,7 @@ private fun LocationContent(
 | 
			
		|||
                            imageOptions = ImageOptions(contentScale = ContentScale.Fit),
 | 
			
		||||
                            item = item,
 | 
			
		||||
                            selectedItem = selectedIndex,
 | 
			
		||||
                            highlight = mapHighlight,
 | 
			
		||||
                            onMarquee = onMarquee,
 | 
			
		||||
                            onTap = onMapTap,
 | 
			
		||||
                        )
 | 
			
		||||
| 
						 | 
				
			
			@ -412,6 +422,7 @@ private fun LocationPreview() {
 | 
			
		|||
                    )
 | 
			
		||||
                },
 | 
			
		||||
                selectedIndex = remember { mutableStateOf(0) },
 | 
			
		||||
                mapHighlight = remember { mutableStateOf(Offset(0.5f, 0.5f)) },
 | 
			
		||||
                onBack = { },
 | 
			
		||||
                onMarquee = { },
 | 
			
		||||
                onMapTap = { },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue