diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt index 3a76d9e14..bbac3c026 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt @@ -267,6 +267,12 @@ fun main() { iconName = "Star", layoutMode = layoutMode, columns = columns, + singlePaneScreens = + if (layoutMode == LayoutMode.SINGLE_PANE) { + columns.map { it.typeKey } + } else { + emptyList() + }, ) workspaceManager.addWorkspace(ws) } @@ -789,12 +795,17 @@ fun App( } LayoutMode.SINGLE_PANE -> { - val screenKey = - ws.singlePaneScreen - ?: ws.columns.firstOrNull()?.typeKey - ?: "home" - val type = DeckState.parseColumnTypeFromKey(screenKey) - if (type != null) singlePaneState.navigate(type) + // Set nav bar screens from workspace + if (ws.singlePaneScreens.isNotEmpty()) { + val screens = + ws.singlePaneScreens.mapNotNull { + DeckState.parseColumnTypeFromKey(it) + } + if (screens.isNotEmpty()) { + pinnedNavBarState.loadFromList(screens) + singlePaneState.navigate(screens.first()) + } + } } } }, diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/AppDrawer.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/AppDrawer.kt index 5a004bba4..0adca7e11 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/AppDrawer.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/AppDrawer.kt @@ -55,6 +55,7 @@ import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.PushPin import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.SportsEsports +import androidx.compose.material.icons.filled.Star import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu @@ -883,7 +884,9 @@ private fun WorkspaceEditorDialog( var columns by remember { mutableStateOf(initial?.columns ?: listOf(Workspace.WorkspaceColumn("home"))) } - var singlePaneScreen by remember { mutableStateOf(initial?.singlePaneScreen) } + var singlePaneScreens by remember { + mutableStateOf(initial?.singlePaneScreens ?: listOf("home")) + } AlertDialog( onDismissRequest = onDismiss, @@ -941,11 +944,11 @@ private fun WorkspaceEditorDialog( if (layoutMode == LayoutMode.DECK) { DeckColumnEditor(columns = columns, onColumnsChange = { columns = it }) } - // Single Pane: screen picker + // Single Pane: nav bar screen selector (first = default) if (layoutMode == LayoutMode.SINGLE_PANE) { - SinglePaneScreenPicker( - selected = singlePaneScreen, - onSelect = { singlePaneScreen = it }, + SinglePaneScreensEditor( + screens = singlePaneScreens, + onScreensChange = { singlePaneScreens = it }, ) } } @@ -963,7 +966,7 @@ private fun WorkspaceEditorDialog( iconName = iconName, layoutMode = layoutMode, columns = columns, - singlePaneScreen = singlePaneScreen, + singlePaneScreens = singlePaneScreens, ), ) }, @@ -1021,29 +1024,52 @@ private fun DeckColumnEditor( } @Composable -private fun SinglePaneScreenPicker( - selected: String?, - onSelect: (String) -> Unit, +private fun SinglePaneScreensEditor( + screens: List, + onScreensChange: (List) -> Unit, ) { - var expanded by remember { mutableStateOf(false) } - val selectedTitle = - selected?.let { DeckState.parseColumnTypeFromKey(it)?.title() } ?: "Select screen" - Column { - Text("Screen", style = MaterialTheme.typography.labelMedium) + Text("Nav Bar Screens", style = MaterialTheme.typography.labelMedium) + Text( + "First screen is the default", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) Spacer(Modifier.height(4.dp)) - TextButton(onClick = { expanded = true }) { Text(selectedTitle) } - DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { - LAUNCHABLE_SCREENS.filter { !it.requiresInput() }.forEach { screen -> - DropdownMenuItem( - text = { Text(screen.title()) }, - onClick = { - onSelect(screen.typeKey()) - expanded = false - }, - ) + screens.forEachIndexed { idx, key -> + val displayName = DeckState.parseColumnTypeFromKey(key)?.title() ?: key + Row(verticalAlignment = Alignment.CenterVertically) { + if (idx == 0) { + Icon( + Icons.Default.Star, + "Default", + Modifier.size(16.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Spacer(Modifier.width(4.dp)) + } + Text(displayName, Modifier.weight(1f)) + IconButton(onClick = { + onScreensChange(screens.toMutableList().apply { removeAt(idx) }) + }) { Icon(Icons.Default.Close, "Remove", Modifier.size(16.dp)) } } } + // Add screen dropdown + var expanded by remember { mutableStateOf(false) } + TextButton(onClick = { expanded = true }) { Text("+ Add Screen") } + DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + PinnedNavBarState.PINNABLE_SCREENS + .filter { screen -> screens.none { it == screen.typeKey() } } + .forEach { screen -> + DropdownMenuItem( + text = { Text(screen.title()) }, + onClick = { + onScreensChange(screens + screen.typeKey()) + expanded = false + }, + ) + } + } } } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt index fe9252dd4..00d5f5eb9 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt @@ -67,6 +67,11 @@ class PinnedNavBarState { DesktopPreferences.pinnedNavItems = _pinnedScreens.value.joinToString(",") { it.typeKey() } } + fun loadFromList(screens: List) { + _pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED } + save() + } + fun load() { val raw = DesktopPreferences.pinnedNavItems if (raw.isBlank()) { diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/Workspace.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/Workspace.kt index 0c3e4f801..0e2d2c62a 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/Workspace.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/Workspace.kt @@ -47,7 +47,7 @@ data class Workspace( val iconName: String, val layoutMode: LayoutMode, val columns: List, - val singlePaneScreen: String? = null, + val singlePaneScreens: List = emptyList(), ) { data class WorkspaceColumn( val typeKey: String, diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/WorkspaceManager.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/WorkspaceManager.kt index d4edbd1e9..2de3e9cf4 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/WorkspaceManager.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/WorkspaceManager.kt @@ -124,7 +124,7 @@ class WorkspaceManager( "name" to ws.name, "iconName" to ws.iconName, "layoutMode" to ws.layoutMode.name, - "singlePaneScreen" to ws.singlePaneScreen, + "singlePaneScreens" to ws.singlePaneScreens, "columns" to ws.columns.map { col -> mapOf( @@ -162,7 +162,14 @@ class WorkspaceManager( } catch (e: Exception) { LayoutMode.DECK } - val singlePaneScreen = entry["singlePaneScreen"] as? String + + @Suppress("UNCHECKED_CAST") + val singlePaneScreens = + (entry["singlePaneScreens"] as? List) ?: run { + // Backward compat: old format had single "singlePaneScreen" string + val legacy = entry["singlePaneScreen"] as? String + if (legacy != null) listOf(legacy) else emptyList() + } @Suppress("UNCHECKED_CAST") val columns = @@ -183,7 +190,7 @@ class WorkspaceManager( iconName = iconName, layoutMode = layoutMode, columns = columns, - singlePaneScreen = singlePaneScreen, + singlePaneScreens = singlePaneScreens, ) } catch (e: Exception) { null