|
@@ -14,28 +14,26 @@ import androidx.compose.animation.fadeIn
|
|
|
import androidx.compose.animation.fadeOut
|
|
import androidx.compose.animation.fadeOut
|
|
|
import androidx.compose.animation.shrinkVertically
|
|
import androidx.compose.animation.shrinkVertically
|
|
|
import androidx.compose.foundation.BorderStroke
|
|
import androidx.compose.foundation.BorderStroke
|
|
|
|
|
+import androidx.compose.foundation.background
|
|
|
import androidx.compose.foundation.border
|
|
import androidx.compose.foundation.border
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.Box
|
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Row
|
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.padding
|
|
|
import androidx.compose.foundation.layout.width
|
|
import androidx.compose.foundation.layout.width
|
|
|
|
|
+import androidx.compose.foundation.shape.CircleShape
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
-import androidx.compose.material.icons.Icons
|
|
|
|
|
-import androidx.compose.material.icons.filled.AccountCircle
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.Composable
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
-import androidx.compose.runtime.MutableState
|
|
|
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
+import androidx.compose.ui.draw.clip
|
|
|
import androidx.compose.ui.focus.FocusRequester
|
|
import androidx.compose.ui.focus.FocusRequester
|
|
|
-import androidx.compose.ui.focus.FocusState
|
|
|
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.Color
|
|
|
import androidx.compose.ui.graphics.Shape
|
|
import androidx.compose.ui.graphics.Shape
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
|
import androidx.compose.ui.unit.Dp
|
|
import androidx.compose.ui.unit.Dp
|
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.dp
|
|
|
-import androidx.tv.material3.DrawerState
|
|
|
|
|
import androidx.tv.material3.DrawerValue
|
|
import androidx.tv.material3.DrawerValue
|
|
|
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
|
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
|
|
import org.mjdev.tvlib.annotations.Previews
|
|
import org.mjdev.tvlib.annotations.Previews
|
|
@@ -44,11 +42,13 @@ import org.mjdev.tvlib.extensions.ComposeExt.isFocused
|
|
|
import org.mjdev.tvlib.extensions.ComposeExt.isLandscapeMode
|
|
import org.mjdev.tvlib.extensions.ComposeExt.isLandscapeMode
|
|
|
import org.mjdev.tvlib.extensions.ComposeExt.isOpen
|
|
import org.mjdev.tvlib.extensions.ComposeExt.isOpen
|
|
|
import org.mjdev.tvlib.extensions.ComposeExt.isPortraitMode
|
|
import org.mjdev.tvlib.extensions.ComposeExt.isPortraitMode
|
|
|
-import org.mjdev.tvlib.extensions.ComposeExt.rememberDrawerState
|
|
|
|
|
import org.mjdev.tvlib.extensions.ComposeExt.rememberFocusRequester
|
|
import org.mjdev.tvlib.extensions.ComposeExt.rememberFocusRequester
|
|
|
import org.mjdev.tvlib.extensions.ComposeExt.rememberFocusState
|
|
import org.mjdev.tvlib.extensions.ComposeExt.rememberFocusState
|
|
|
import org.mjdev.tvlib.extensions.ModifierExt.focusState
|
|
import org.mjdev.tvlib.extensions.ModifierExt.focusState
|
|
|
-import org.mjdev.tvlib.extensions.ModifierExt.recomposeHighlighter
|
|
|
|
|
|
|
+import org.mjdev.tvlib.extensions.NavControllerExt.open
|
|
|
|
|
+import org.mjdev.tvlib.extensions.NavExt.rememberNavControllerEx
|
|
|
|
|
+import org.mjdev.tvlib.navigation.MenuItem
|
|
|
|
|
+import org.mjdev.tvlib.navigation.NavHostControllerEx
|
|
|
import org.mjdev.tvlib.ui.components.card.FocusHelper
|
|
import org.mjdev.tvlib.ui.components.card.FocusHelper
|
|
|
import org.mjdev.tvlib.ui.components.complex.FocusableBox
|
|
import org.mjdev.tvlib.ui.components.complex.FocusableBox
|
|
|
import org.mjdev.tvlib.ui.components.icon.IconAny
|
|
import org.mjdev.tvlib.ui.components.icon.IconAny
|
|
@@ -60,9 +60,6 @@ import timber.log.Timber
|
|
|
@Composable
|
|
@Composable
|
|
|
fun NavigationRow(
|
|
fun NavigationRow(
|
|
|
modifier: Modifier = Modifier,
|
|
modifier: Modifier = Modifier,
|
|
|
- id: Int = -1,
|
|
|
|
|
- text: Any? = "menu item 1",
|
|
|
|
|
- icon: Any? = Icons.Default.AccountCircle,
|
|
|
|
|
textColor: Color = Color.White,
|
|
textColor: Color = Color.White,
|
|
|
iconColor: Color = Color.White,
|
|
iconColor: Color = Color.White,
|
|
|
focusedColor: Color = Color.Green,
|
|
focusedColor: Color = Color.Green,
|
|
@@ -73,16 +70,26 @@ fun NavigationRow(
|
|
|
strokeWidth: Dp = 1.dp,
|
|
strokeWidth: Dp = 1.dp,
|
|
|
margin: Dp = 0.dp,
|
|
margin: Dp = 0.dp,
|
|
|
padding: Dp = 4.dp,
|
|
padding: Dp = 4.dp,
|
|
|
- drawerState: DrawerState = rememberDrawerState(),
|
|
|
|
|
- focused: Boolean = isEditMode(),
|
|
|
|
|
- focusState: MutableState<FocusState?> = rememberFocusState(
|
|
|
|
|
- text,
|
|
|
|
|
- FocusHelper(focused)
|
|
|
|
|
- ),
|
|
|
|
|
- focusRequester: FocusRequester = rememberFocusRequester(id),
|
|
|
|
|
- onFocus: (id: Int) -> Unit = {},
|
|
|
|
|
- onClick: (id: Int) -> Unit = {},
|
|
|
|
|
|
|
+ navController: NavHostControllerEx = rememberNavControllerEx(),
|
|
|
|
|
+ menuItem: MenuItem = MenuItem.MENU_ITEM_EXIT,
|
|
|
) {
|
|
) {
|
|
|
|
|
+ val id = navController.indexOfMenuItem(menuItem)
|
|
|
|
|
+ val focusRequester: FocusRequester = rememberFocusRequester()
|
|
|
|
|
+ val onDrawerItemFocus: () -> Unit = {
|
|
|
|
|
+ navController.menuItem(id).let { menuItem ->
|
|
|
|
|
+ if (menuItem.isPage) {
|
|
|
|
|
+ navController.onMenuItemClick(menuItem)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ val onFocus: () -> Unit = {
|
|
|
|
|
+ navController.openMenu()
|
|
|
|
|
+ navController.selectedMenuItem.intValue = id
|
|
|
|
|
+ onDrawerItemFocus()
|
|
|
|
|
+ }
|
|
|
|
|
+ val drawerState = navController.menuDrawerState
|
|
|
|
|
+ val focused = (navController.selectedMenuItem.intValue == id)
|
|
|
|
|
+ val focusState = rememberFocusState(FocusHelper(focused))
|
|
|
val isEdit = isEditMode()
|
|
val isEdit = isEditMode()
|
|
|
val isShown = isLandscapeMode() || drawerState.isOpen
|
|
val isShown = isLandscapeMode() || drawerState.isOpen
|
|
|
val isPortrait = isPortraitMode()
|
|
val isPortrait = isPortraitMode()
|
|
@@ -90,16 +97,27 @@ fun NavigationRow(
|
|
|
else (fadeIn() + expandVertically())
|
|
else (fadeIn() + expandVertically())
|
|
|
val exit = if (isPortrait) shrinkVertically()
|
|
val exit = if (isPortrait) shrinkVertically()
|
|
|
else (fadeOut() + shrinkVertically())
|
|
else (fadeOut() + shrinkVertically())
|
|
|
|
|
+ val onClick: () -> Unit = {
|
|
|
|
|
+ navController.menuItem(id).let { menuItem ->
|
|
|
|
|
+ if (menuItem.isAction) {
|
|
|
|
|
+ menuItem.menuAction?.invoke()
|
|
|
|
|
+ }
|
|
|
|
|
+ if (menuItem.isRoute) {
|
|
|
|
|
+ navController.open(menuItem.menuRoute)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (menuItem.isPage) {
|
|
|
|
|
+ navController.onMenuItemClick(menuItem)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
if (isShown) {
|
|
if (isShown) {
|
|
|
FocusableBox(
|
|
FocusableBox(
|
|
|
modifier = modifier
|
|
modifier = modifier
|
|
|
- .recomposeHighlighter()
|
|
|
|
|
.padding(margin)
|
|
.padding(margin)
|
|
|
.border(
|
|
.border(
|
|
|
BorderStroke(
|
|
BorderStroke(
|
|
|
strokeWidth,
|
|
strokeWidth,
|
|
|
- if (isEdit || focusState.isFocused) focusedColor
|
|
|
|
|
- else unFocusedColor
|
|
|
|
|
|
|
+ if (isEdit || focused) focusedColor else unFocusedColor
|
|
|
),
|
|
),
|
|
|
shape
|
|
shape
|
|
|
)
|
|
)
|
|
@@ -110,27 +128,27 @@ fun NavigationRow(
|
|
|
unFocusedColor = unFocusedColor,
|
|
unFocusedColor = unFocusedColor,
|
|
|
onFocusChange = { state ->
|
|
onFocusChange = { state ->
|
|
|
if (state.isFocused || state.hasFocus) {
|
|
if (state.isFocused || state.hasFocus) {
|
|
|
- onFocus(id)
|
|
|
|
|
|
|
+ onFocus()
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- onClick = {
|
|
|
|
|
- onClick(id)
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ onClick = { onClick() },
|
|
|
) {
|
|
) {
|
|
|
Row(
|
|
Row(
|
|
|
- modifier = Modifier
|
|
|
|
|
- .recomposeHighlighter()
|
|
|
|
|
- .padding(padding),
|
|
|
|
|
|
|
+ modifier = Modifier.padding(padding),
|
|
|
horizontalArrangement = Arrangement.Start,
|
|
horizontalArrangement = Arrangement.Start,
|
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
|
) {
|
|
) {
|
|
|
Box(
|
|
Box(
|
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
|
.padding(4.dp)
|
|
.padding(4.dp)
|
|
|
- .recomposeHighlighter()
|
|
|
|
|
|
|
+ .clip(CircleShape)
|
|
|
|
|
+ .background(
|
|
|
|
|
+ if (isEdit || focusState.isFocused) focusedColor else unFocusedColor,
|
|
|
|
|
+ CircleShape
|
|
|
|
|
+ )
|
|
|
) {
|
|
) {
|
|
|
IconAny(
|
|
IconAny(
|
|
|
- src = icon,
|
|
|
|
|
|
|
+ src = menuItem.menuIcon,
|
|
|
contentDescription = null,
|
|
contentDescription = null,
|
|
|
tint = iconColor
|
|
tint = iconColor
|
|
|
)
|
|
)
|
|
@@ -141,10 +159,9 @@ fun NavigationRow(
|
|
|
visible = (isEdit || (drawerState.currentValue == DrawerValue.Open))
|
|
visible = (isEdit || (drawerState.currentValue == DrawerValue.Open))
|
|
|
) {
|
|
) {
|
|
|
TextAny(
|
|
TextAny(
|
|
|
- text = text,
|
|
|
|
|
|
|
+ text = menuItem.menuText,
|
|
|
softWrap = false,
|
|
softWrap = false,
|
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
|
- .recomposeHighlighter()
|
|
|
|
|
.padding(8.dp)
|
|
.padding(8.dp)
|
|
|
.width(expandedWidth),
|
|
.width(expandedWidth),
|
|
|
color = textColor,
|
|
color = textColor,
|