|
|
|
@@ -13,18 +13,23 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.platform.testTag
|
|
|
|
import androidx.compose.ui.platform.testTag
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
|
|
|
|
import androidx.navigation.NavHostController
|
|
|
|
|
|
|
|
import androidx.navigation.compose.NavHost
|
|
|
|
|
|
|
|
import androidx.navigation.compose.composable
|
|
|
|
|
|
|
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
|
|
|
|
|
|
import androidx.navigation.compose.rememberNavController
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
|
|
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
|
|
|
import org.shahondin1624.lib.components.TestTags
|
|
|
|
import org.shahondin1624.lib.components.TestTags
|
|
|
|
import org.shahondin1624.lib.components.UiConstants
|
|
|
|
import org.shahondin1624.lib.components.UiConstants
|
|
|
|
import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage
|
|
|
|
import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage
|
|
|
|
|
|
|
|
import org.shahondin1624.lib.components.settings.SettingsPage
|
|
|
|
import org.shahondin1624.model.EXAMPLE_CHARACTER
|
|
|
|
import org.shahondin1624.model.EXAMPLE_CHARACTER
|
|
|
|
|
|
|
|
import org.shahondin1624.navigation.AppRoutes
|
|
|
|
import org.shahondin1624.theme.AppTheme
|
|
|
|
import org.shahondin1624.theme.AppTheme
|
|
|
|
import org.shahondin1624.theme.LocalThemeIsDark
|
|
|
|
import org.shahondin1624.theme.LocalThemeIsDark
|
|
|
|
import org.shahondin1624.theme.LocalWindowSizeClass
|
|
|
|
import org.shahondin1624.theme.LocalWindowSizeClass
|
|
|
|
@@ -40,20 +45,44 @@ fun App(
|
|
|
|
val windowSizeClass = WindowSizeClass.fromWidth(maxWidth)
|
|
|
|
val windowSizeClass = WindowSizeClass.fromWidth(maxWidth)
|
|
|
|
|
|
|
|
|
|
|
|
CompositionLocalProvider(LocalWindowSizeClass provides windowSizeClass) {
|
|
|
|
CompositionLocalProvider(LocalWindowSizeClass provides windowSizeClass) {
|
|
|
|
AppContent()
|
|
|
|
val navController = rememberNavController()
|
|
|
|
|
|
|
|
AppContent(navController)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun AppContent() {
|
|
|
|
private fun AppContent(navController: NavHostController) {
|
|
|
|
val windowSizeClass = LocalWindowSizeClass.current
|
|
|
|
val windowSizeClass = LocalWindowSizeClass.current
|
|
|
|
|
|
|
|
|
|
|
|
when (windowSizeClass) {
|
|
|
|
when (windowSizeClass) {
|
|
|
|
WindowSizeClass.Compact -> CompactNavigation()
|
|
|
|
WindowSizeClass.Compact -> CompactNavigation(navController)
|
|
|
|
WindowSizeClass.Medium -> MediumNavigation()
|
|
|
|
WindowSizeClass.Medium -> MediumNavigation(navController)
|
|
|
|
WindowSizeClass.Expanded -> ExpandedNavigation()
|
|
|
|
WindowSizeClass.Expanded -> ExpandedNavigation(navController)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the current route from the NavController's back stack.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
|
|
|
private fun currentRoute(navController: NavHostController): String? {
|
|
|
|
|
|
|
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
|
|
|
|
|
|
return navBackStackEntry?.destination?.route
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Navigate to a route, popping back to the start destination to avoid
|
|
|
|
|
|
|
|
* building up a deep back stack.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private fun navigateTo(navController: NavHostController, route: String) {
|
|
|
|
|
|
|
|
navController.navigate(route) {
|
|
|
|
|
|
|
|
popUpTo(AppRoutes.CHARACTER_SHEET) {
|
|
|
|
|
|
|
|
saveState = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
launchSingleTop = true
|
|
|
|
|
|
|
|
restoreState = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -62,19 +91,27 @@ private fun AppContent() {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun CompactNavigation() {
|
|
|
|
private fun CompactNavigation(navController: NavHostController) {
|
|
|
|
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
|
|
|
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
|
|
|
val scope = rememberCoroutineScope()
|
|
|
|
val scope = rememberCoroutineScope()
|
|
|
|
|
|
|
|
val currentRoute = currentRoute(navController)
|
|
|
|
|
|
|
|
|
|
|
|
ModalNavigationDrawer(
|
|
|
|
ModalNavigationDrawer(
|
|
|
|
drawerState = drawerState,
|
|
|
|
drawerState = drawerState,
|
|
|
|
drawerContent = {
|
|
|
|
drawerContent = {
|
|
|
|
ModalDrawerSheet {
|
|
|
|
ModalDrawerSheet {
|
|
|
|
DrawerContent()
|
|
|
|
DrawerContent(
|
|
|
|
|
|
|
|
currentRoute = currentRoute,
|
|
|
|
|
|
|
|
onNavigate = { route ->
|
|
|
|
|
|
|
|
navigateTo(navController, route)
|
|
|
|
|
|
|
|
scope.launch { drawerState.close() }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
MainScaffold(
|
|
|
|
MainScaffold(
|
|
|
|
|
|
|
|
navController = navController,
|
|
|
|
showMenuButton = true,
|
|
|
|
showMenuButton = true,
|
|
|
|
onMenuClick = {
|
|
|
|
onMenuClick = {
|
|
|
|
scope.launch {
|
|
|
|
scope.launch {
|
|
|
|
@@ -90,7 +127,9 @@ private fun CompactNavigation() {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun MediumNavigation() {
|
|
|
|
private fun MediumNavigation(navController: NavHostController) {
|
|
|
|
|
|
|
|
val currentRoute = currentRoute(navController)
|
|
|
|
|
|
|
|
|
|
|
|
Row(modifier = Modifier.fillMaxSize()) {
|
|
|
|
Row(modifier = Modifier.fillMaxSize()) {
|
|
|
|
NavigationRail(
|
|
|
|
NavigationRail(
|
|
|
|
modifier = Modifier.testTag(TestTags.NAV_RAIL)
|
|
|
|
modifier = Modifier.testTag(TestTags.NAV_RAIL)
|
|
|
|
@@ -99,19 +138,20 @@ private fun MediumNavigation() {
|
|
|
|
NavigationRailItem(
|
|
|
|
NavigationRailItem(
|
|
|
|
icon = { Icon(Icons.Default.Person, contentDescription = null) },
|
|
|
|
icon = { Icon(Icons.Default.Person, contentDescription = null) },
|
|
|
|
label = { Text("Character") },
|
|
|
|
label = { Text("Character") },
|
|
|
|
selected = true,
|
|
|
|
selected = currentRoute == AppRoutes.CHARACTER_SHEET,
|
|
|
|
onClick = { /* TODO: Navigate */ },
|
|
|
|
onClick = { navigateTo(navController, AppRoutes.CHARACTER_SHEET) },
|
|
|
|
modifier = Modifier.testTag(TestTags.NAV_CHARACTER_SHEET)
|
|
|
|
modifier = Modifier.testTag(TestTags.NAV_CHARACTER_SHEET)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
NavigationRailItem(
|
|
|
|
NavigationRailItem(
|
|
|
|
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
|
|
|
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
|
|
|
label = { Text("Settings") },
|
|
|
|
label = { Text("Settings") },
|
|
|
|
selected = false,
|
|
|
|
selected = currentRoute == AppRoutes.SETTINGS,
|
|
|
|
onClick = { /* TODO: Navigate to settings */ },
|
|
|
|
onClick = { navigateTo(navController, AppRoutes.SETTINGS) },
|
|
|
|
modifier = Modifier.testTag(TestTags.NAV_SETTINGS)
|
|
|
|
modifier = Modifier.testTag(TestTags.NAV_SETTINGS)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MainScaffold(
|
|
|
|
MainScaffold(
|
|
|
|
|
|
|
|
navController = navController,
|
|
|
|
showMenuButton = false,
|
|
|
|
showMenuButton = false,
|
|
|
|
onMenuClick = {},
|
|
|
|
onMenuClick = {},
|
|
|
|
modifier = Modifier.weight(1f)
|
|
|
|
modifier = Modifier.weight(1f)
|
|
|
|
@@ -124,15 +164,21 @@ private fun MediumNavigation() {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun ExpandedNavigation() {
|
|
|
|
private fun ExpandedNavigation(navController: NavHostController) {
|
|
|
|
|
|
|
|
val currentRoute = currentRoute(navController)
|
|
|
|
|
|
|
|
|
|
|
|
PermanentNavigationDrawer(
|
|
|
|
PermanentNavigationDrawer(
|
|
|
|
drawerContent = {
|
|
|
|
drawerContent = {
|
|
|
|
PermanentDrawerSheet(modifier = Modifier.testTag(TestTags.NAV_PERMANENT_DRAWER)) {
|
|
|
|
PermanentDrawerSheet(modifier = Modifier.testTag(TestTags.NAV_PERMANENT_DRAWER)) {
|
|
|
|
DrawerContent()
|
|
|
|
DrawerContent(
|
|
|
|
|
|
|
|
currentRoute = currentRoute,
|
|
|
|
|
|
|
|
onNavigate = { route -> navigateTo(navController, route) }
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
MainScaffold(
|
|
|
|
MainScaffold(
|
|
|
|
|
|
|
|
navController = navController,
|
|
|
|
showMenuButton = false,
|
|
|
|
showMenuButton = false,
|
|
|
|
onMenuClick = {}
|
|
|
|
onMenuClick = {}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
@@ -143,7 +189,10 @@ private fun ExpandedNavigation() {
|
|
|
|
* Shared drawer content used by Compact (ModalDrawerSheet) and Expanded (PermanentDrawerSheet).
|
|
|
|
* Shared drawer content used by Compact (ModalDrawerSheet) and Expanded (PermanentDrawerSheet).
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun DrawerContent() {
|
|
|
|
private fun DrawerContent(
|
|
|
|
|
|
|
|
currentRoute: String?,
|
|
|
|
|
|
|
|
onNavigate: (String) -> Unit
|
|
|
|
|
|
|
|
) {
|
|
|
|
Spacer(Modifier.height(16.dp))
|
|
|
|
Spacer(Modifier.height(16.dp))
|
|
|
|
Text(
|
|
|
|
Text(
|
|
|
|
"Menu",
|
|
|
|
"Menu",
|
|
|
|
@@ -156,8 +205,8 @@ private fun DrawerContent() {
|
|
|
|
NavigationDrawerItem(
|
|
|
|
NavigationDrawerItem(
|
|
|
|
icon = { Icon(Icons.Default.Person, contentDescription = null) },
|
|
|
|
icon = { Icon(Icons.Default.Person, contentDescription = null) },
|
|
|
|
label = { Text("Character Sheet") },
|
|
|
|
label = { Text("Character Sheet") },
|
|
|
|
selected = true,
|
|
|
|
selected = currentRoute == AppRoutes.CHARACTER_SHEET,
|
|
|
|
onClick = { /* TODO: Navigate */ },
|
|
|
|
onClick = { onNavigate(AppRoutes.CHARACTER_SHEET) },
|
|
|
|
modifier = Modifier
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(horizontal = 12.dp, vertical = 4.dp)
|
|
|
|
.padding(horizontal = 12.dp, vertical = 4.dp)
|
|
|
|
.testTag(TestTags.NAV_CHARACTER_SHEET)
|
|
|
|
.testTag(TestTags.NAV_CHARACTER_SHEET)
|
|
|
|
@@ -166,8 +215,8 @@ private fun DrawerContent() {
|
|
|
|
NavigationDrawerItem(
|
|
|
|
NavigationDrawerItem(
|
|
|
|
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
|
|
|
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
|
|
|
label = { Text("Settings") },
|
|
|
|
label = { Text("Settings") },
|
|
|
|
selected = false,
|
|
|
|
selected = currentRoute == AppRoutes.SETTINGS,
|
|
|
|
onClick = { /* TODO: Navigate to settings */ },
|
|
|
|
onClick = { onNavigate(AppRoutes.SETTINGS) },
|
|
|
|
modifier = Modifier
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(horizontal = 12.dp, vertical = 4.dp)
|
|
|
|
.padding(horizontal = 12.dp, vertical = 4.dp)
|
|
|
|
.testTag(TestTags.NAV_SETTINGS)
|
|
|
|
.testTag(TestTags.NAV_SETTINGS)
|
|
|
|
@@ -175,8 +224,9 @@ private fun DrawerContent() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Main scaffold with top app bar and content area.
|
|
|
|
* Main scaffold with top app bar and routed content area.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param navController the NavHostController for page routing
|
|
|
|
* @param showMenuButton whether to show the hamburger menu icon (hidden when nav is persistent)
|
|
|
|
* @param showMenuButton whether to show the hamburger menu icon (hidden when nav is persistent)
|
|
|
|
* @param onMenuClick callback for hamburger menu button click
|
|
|
|
* @param onMenuClick callback for hamburger menu button click
|
|
|
|
* @param modifier optional modifier (e.g., weight in Row for Medium layout)
|
|
|
|
* @param modifier optional modifier (e.g., weight in Row for Medium layout)
|
|
|
|
@@ -184,11 +234,11 @@ private fun DrawerContent() {
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun MainScaffold(
|
|
|
|
private fun MainScaffold(
|
|
|
|
|
|
|
|
navController: NavHostController,
|
|
|
|
showMenuButton: Boolean,
|
|
|
|
showMenuButton: Boolean,
|
|
|
|
onMenuClick: () -> Unit,
|
|
|
|
onMenuClick: () -> Unit,
|
|
|
|
modifier: Modifier = Modifier
|
|
|
|
modifier: Modifier = Modifier
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val character = EXAMPLE_CHARACTER
|
|
|
|
|
|
|
|
var isDark by LocalThemeIsDark.current
|
|
|
|
var isDark by LocalThemeIsDark.current
|
|
|
|
|
|
|
|
|
|
|
|
Scaffold(
|
|
|
|
Scaffold(
|
|
|
|
@@ -235,14 +285,25 @@ private fun MainScaffold(
|
|
|
|
.windowInsetsPadding(WindowInsets.safeDrawing),
|
|
|
|
.windowInsetsPadding(WindowInsets.safeDrawing),
|
|
|
|
contentAlignment = Alignment.TopCenter
|
|
|
|
contentAlignment = Alignment.TopCenter
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
Column(
|
|
|
|
NavHost(
|
|
|
|
|
|
|
|
navController = navController,
|
|
|
|
|
|
|
|
startDestination = AppRoutes.CHARACTER_SHEET,
|
|
|
|
modifier = Modifier
|
|
|
|
modifier = Modifier
|
|
|
|
.widthIn(max = UiConstants.MAX_CONTENT_WIDTH)
|
|
|
|
.widthIn(max = UiConstants.MAX_CONTENT_WIDTH)
|
|
|
|
.fillMaxWidth()
|
|
|
|
.fillMaxWidth()
|
|
|
|
.padding(contentPadding),
|
|
|
|
.padding(contentPadding)
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
AttributesPage(character)
|
|
|
|
composable(AppRoutes.CHARACTER_SHEET) {
|
|
|
|
|
|
|
|
Column(
|
|
|
|
|
|
|
|
modifier = Modifier.fillMaxSize(),
|
|
|
|
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
AttributesPage(EXAMPLE_CHARACTER)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
composable(AppRoutes.SETTINGS) {
|
|
|
|
|
|
|
|
SettingsPage()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|