Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sheaf/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
// Adaptive top-level navigation: bottom bar on phones, rail on wide screens.
implementation(libs.androidx.material3.adaptive.navigation.suite)
implementation(libs.androidx.material.icons.extended)
implementation(libs.androidx.navigation.compose)

Expand Down
3 changes: 1 addition & 2 deletions sheaf/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait">
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
92 changes: 63 additions & 29 deletions sheaf/app/src/main/java/systems/lupine/sheaf/ui/SheafApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@ package systems.lupine.sheaf.ui

import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.automirrored.outlined.MenuBook
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import android.net.Uri
Expand Down Expand Up @@ -139,6 +148,15 @@ data class TopLevelDest(
val unselectedIcon: ImageVector,
)

// Widest a single content pane grows to; beyond this, centre it with gutters
// rather than stretching forms and lists across a whole tablet. A no-op on
// anything narrower (phones, split-screen).
private val MAX_CONTENT_WIDTH = 840.dp

// Destinations that should use the full window width rather than the capped
// content pane (a pan/zoom canvas wants all the room it can get).
private val FULL_BLEED_ROUTES = setOf(Routes.RELATIONSHIP_GRAPH)

val topLevelDestinations = listOf(
TopLevelDest(Routes.HOME, "Home", Icons.Filled.Home, Icons.Outlined.Home),
TopLevelDest(Routes.PEOPLE, "Members", Icons.Filled.People, Icons.Outlined.People),
Expand Down Expand Up @@ -204,41 +222,56 @@ fun SheafApp(
LocalFileCdnBase provides fileCdnBase,
LocalDisplayTimeZone provides displayZone,
) {
Scaffold(
bottomBar = {
if (showBottomBar) {
NavigationBar {
val currentDest = navBackStack?.destination
topLevelDestinations.forEach { dest ->
val selected = currentDest?.hierarchy?.any { it.route == dest.route } == true
NavigationBarItem(
selected = selected,
onClick = {
navController.navigate(dest.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
Icon(
if (selected) dest.selectedIcon else dest.unselectedIcon,
contentDescription = dest.label,
)
},
label = { Text(dest.label) },
// Adaptive top-level chrome: a bottom bar on compact widths (phones), a
// navigation rail on wider ones (tablets, landscape, foldables, large
// windows), and nothing at all on non-top-level (full-screen) destinations.
// 600dp is the standard compact/medium width breakpoint.
val currentDest = navBackStack?.destination
val wide = LocalConfiguration.current.screenWidthDp >= 600
val navSuiteType = when {
!showBottomBar -> NavigationSuiteType.None
wide -> NavigationSuiteType.NavigationRail
else -> NavigationSuiteType.NavigationBar
}
NavigationSuiteScaffold(
layoutType = navSuiteType,
navigationSuiteItems = {
topLevelDestinations.forEach { dest ->
val selected = currentDest?.hierarchy?.any { it.route == dest.route } == true
item(
selected = selected,
onClick = {
navController.navigate(dest.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
Icon(
if (selected) dest.selectedIcon else dest.unselectedIcon,
contentDescription = dest.label,
)
}
}
},
label = { Text(dest.label) },
)
}
},
) { innerPadding ->
) {
// Cap content width on wide windows so forms and lists don't stretch
// across a whole tablet; centre what remains. widthIn is a no-op below
// the cap, so phones are unaffected. Full-bleed screens (the pan/zoom
// relationship graph) opt out and use the whole area.
val fullBleed = currentRoute in FULL_BLEED_ROUTES
val contentModifier = if (fullBleed) Modifier.fillMaxSize()
else Modifier.fillMaxHeight().widthIn(max = MAX_CONTENT_WIDTH)
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
NavHost(
navController = navController,
startDestination = Routes.LOGIN,
modifier = Modifier.padding(innerPadding),
modifier = contentModifier,
enterTransition = { fadeIn() },
exitTransition = { fadeOut() },
popEnterTransition = { fadeIn() },
Expand Down Expand Up @@ -675,6 +708,7 @@ fun SheafApp(
)
}
}
}
}
}
}
1 change: 1 addition & 0 deletions sheaf/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-material3-adaptive-navigation-suite = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite", version.ref = "material3" }
androidx-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
Expand Down
Loading