2
0
milan jurkulák 2 жил өмнө
parent
commit
08823c5365

+ 69 - 0
src/main/kotlin/eu/mjdev/desktop/components/immersivelist/ImmersiveInnerList.kt

@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package eu.mjdev.desktop.components.immersivelist
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Brush.Companion.verticalGradient
+import androidx.compose.ui.graphics.Color
+import eu.mjdev.desktop.extensions.ColorUtils.createVerticalColorBrush
+import eu.mjdev.desktop.helpers.compose.Gravity
+
+@Composable
+fun ImmersiveListScope.ImmersiveInnerList(
+    modifier: Modifier = Modifier,
+    visible: Boolean = true,
+    backgroundColor: Color = Color.Transparent,
+    enter: EnterTransition = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }),
+    exit: ExitTransition = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 }),
+    content: @Composable ImmersiveListScope.() -> Unit = {},
+) {
+    val background: State<Brush> = remember(backgroundColor) {
+        derivedStateOf {
+            if (backgroundColor == Color.Transparent) {
+                verticalGradient(
+                    listOf(
+                        Color.Transparent,
+                        Color.Transparent
+                    )
+                )
+            } else {
+                createVerticalColorBrush(
+                    backgroundColor,
+                    Gravity.BOTTOM
+                )
+            }
+        }
+    }
+    AnimatedVisibility(
+        modifier = modifier,
+        visible = visible,
+        enter = enter,
+        exit = exit
+    ) {
+        Box(
+            modifier = modifier.background(background.value)
+        ) {
+            content()
+        }
+    }
+}

+ 53 - 0
src/main/kotlin/eu/mjdev/desktop/components/immersivelist/ImmersiveList.kt

@@ -0,0 +1,53 @@
+package eu.mjdev.desktop.components.immersivelist
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableIntState
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.platform.LocalFocusManager
+import eu.mjdev.desktop.extensions.Compose.bringIntoViewIfChildrenAreFocused
+
+@Suppress("IllegalExperimentalApiUsage")
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun ImmersiveList(
+    background: @Composable ImmersiveListBackgroundScope.(
+        index: Int,
+        listHasFocus: Boolean
+    ) -> Unit = { _, _ -> },
+    modifier: Modifier = Modifier,
+    listAlignment: Alignment = Alignment.BottomEnd,
+    currentItemIndex: MutableIntState = remember { mutableIntStateOf(0) },
+    listHasFocus: MutableState<Boolean> = remember { mutableStateOf(false) },
+    list: @Composable ImmersiveListScope.() -> Unit = {},
+) = Box(
+    modifier.bringIntoViewIfChildrenAreFocused()
+) {
+    ImmersiveListBackgroundScope(
+        this
+    ).background(
+        currentItemIndex.intValue,
+        listHasFocus.value
+    )
+    val focusManager = LocalFocusManager.current
+    Box(
+        Modifier
+            .align(listAlignment)
+            .onFocusChanged { focusState ->
+                listHasFocus.value = focusState.hasFocus
+            }
+    ) {
+        ImmersiveListScope { idx ->
+            currentItemIndex.intValue = idx
+            focusManager.moveFocus(FocusDirection.Enter)
+        }.list()
+    }
+}

+ 61 - 0
src/main/kotlin/eu/mjdev/desktop/components/immersivelist/ImmersiveListBackgroundScope.kt

@@ -0,0 +1,61 @@
+package eu.mjdev.desktop.components.immersivelist
+
+import androidx.compose.animation.AnimatedContentTransitionScope
+import androidx.compose.animation.AnimatedVisibilityScope
+import androidx.compose.animation.ContentTransform
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+
+@Immutable
+class ImmersiveListBackgroundScope(
+    boxScope: BoxScope
+) : BoxScope by boxScope {
+
+    @Suppress("unused")
+    @Composable
+    fun AnimatedVisibility(
+        visible: Boolean,
+        modifier: Modifier = Modifier,
+        enter: EnterTransition = ImmersiveListDefaults.EnterTransition,
+        exit: ExitTransition = ImmersiveListDefaults.ExitTransition,
+        label: String = "AnimatedVisibility",
+        content: @Composable AnimatedVisibilityScope.() -> Unit
+    ) {
+        androidx.compose.animation.AnimatedVisibility(
+            visible,
+            modifier,
+            enter,
+            exit,
+            label,
+            content
+        )
+    }
+
+    @Suppress("unused")
+    @Composable
+    fun AnimatedContent(
+        targetState: Int,
+        modifier: Modifier = Modifier,
+        transitionSpec: AnimatedContentTransitionScope<Int>.() -> ContentTransform = {
+            ImmersiveListDefaults.EnterTransition.togetherWith(ImmersiveListDefaults.ExitTransition)
+        },
+        contentAlignment: Alignment = Alignment.TopStart,
+        label: String = "AnimatedContent",
+        content: @Composable AnimatedVisibilityScope.(targetState: Int) -> Unit
+    ) {
+        androidx.compose.animation.AnimatedContent(
+            targetState,
+            modifier,
+            transitionSpec,
+            contentAlignment,
+            content = content,
+            label = label
+        )
+    }
+}

+ 12 - 0
src/main/kotlin/eu/mjdev/desktop/components/immersivelist/ImmersiveListDefaults.kt

@@ -0,0 +1,12 @@
+package eu.mjdev.desktop.components.immersivelist
+
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+
+object ImmersiveListDefaults {
+    val EnterTransition: EnterTransition = fadeIn(animationSpec = tween(300))
+    val ExitTransition: ExitTransition = fadeOut(animationSpec = tween(300))
+}

+ 16 - 0
src/main/kotlin/eu/mjdev/desktop/components/immersivelist/ImmersiveListScope.kt

@@ -0,0 +1,16 @@
+package eu.mjdev.desktop.components.immersivelist
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+
+@Immutable
+class ImmersiveListScope internal constructor(
+    private val onFocused: (Int) -> Unit
+) {
+    fun Modifier.immersiveListItem(index: Int): Modifier {
+        return this then onFocusChanged {
+            if (it.isFocused) onFocused(index)
+        }
+    }
+}