diff --git a/app/src/main/kotlin/com/makefunstuff/herdr/AppNav.kt b/app/src/main/kotlin/com/makefunstuff/herdr/AppNav.kt index e385a803..cbf8634e 100644 --- a/app/src/main/kotlin/com/makefunstuff/herdr/AppNav.kt +++ b/app/src/main/kotlin/com/makefunstuff/herdr/AppNav.kt @@ -274,10 +274,10 @@ private fun AppNav(nav: NavHostController, startSpace: String? = null) { val panes = remember(wide) { Panes(wide) } val current by rememberUpdatedState(panes) LaunchedEffect(nav) { Links.flow.collect { current.link(nav, it) } } - // A space picked in the drawer shows in the Chat tab. + // A space picked in the drawer (or the sidebar) shows in the Chat tab. fun pick(slot: SpaceSlot) { space.key = slot.key - nav.goTop(Routes.CHAT) + nav.showSpace() } val openDrawer: () -> Unit = { scope.launch { drawer.open() } } val lineColor = Herdr.colors.lineStrong @@ -445,6 +445,22 @@ private fun PaneEdge() { Box(Modifier.width(1.dp).fillMaxHeight().background(Herdr.colors.line)) } +/** + * The Chat tab's own screen, for a space just picked: not what the tab had open over it (that was the last space's + * terminal or chat). A phone's drawer opens only over a tab's own screen; the sidebar is there over any (issue #101). + */ +private fun NavHostController.showSpace() { + if (currentDestination?.route == Routes.CHAT) return + val open = currentBackStack.value.any { it.destination.route == Routes.CHAT } + // Left for another tab, the Chat tab's screens were kept to come back to: they go. + if (!open) clearBackStack(Routes.CHAT) + navigate(Routes.CHAT) { + // Open, they go too; another tab's are kept, as a tab's switch keeps them. + popUpTo(graph.findStartDestination().id) { saveState = !open } + launchSingleTop = true + } +} + private fun NavHostController.goTop(route: String) { navigate(route) { popUpTo(graph.findStartDestination().id) { saveState = true } diff --git a/docs/screenshots/herd-35/after/wide-pick-light.png b/docs/screenshots/herd-35/after/wide-pick-light.png new file mode 100644 index 00000000..7ed45ca3 Binary files /dev/null and b/docs/screenshots/herd-35/after/wide-pick-light.png differ diff --git a/docs/screenshots/herd-35/after/wide-pick.png b/docs/screenshots/herd-35/after/wide-pick.png new file mode 100644 index 00000000..e0e2cc2e Binary files /dev/null and b/docs/screenshots/herd-35/after/wide-pick.png differ diff --git a/docs/screenshots/herd-35/before/wide-pick-light.png b/docs/screenshots/herd-35/before/wide-pick-light.png new file mode 100644 index 00000000..c5ffda77 Binary files /dev/null and b/docs/screenshots/herd-35/before/wide-pick-light.png differ diff --git a/docs/screenshots/herd-35/before/wide-pick.png b/docs/screenshots/herd-35/before/wide-pick.png new file mode 100644 index 00000000..20cc4934 Binary files /dev/null and b/docs/screenshots/herd-35/before/wide-pick.png differ diff --git a/tools/ui-preview/src/main/kotlin/preview/Main.kt b/tools/ui-preview/src/main/kotlin/preview/Main.kt index 67766ee7..21db9ef9 100644 --- a/tools/ui-preview/src/main/kotlin/preview/Main.kt +++ b/tools/ui-preview/src/main/kotlin/preview/Main.kt @@ -329,6 +329,16 @@ private fun ComposeUiTest.node(matcher: SemanticsMatcher) = onAllNodes(matcher, /** Taps the first element showing [text] (exactly, or as its start when it is longer). */ fun ComposeUiTest.tapText(text: String) { tap(node(hasText(text, substring = true))) } +/** Fails the scene unless an element shows [text]: a scene that checks what a tap leads to. */ +fun ComposeUiTest.expectText(text: String) { + check(onAllNodes(hasText(text, substring = true), useUnmergedTree = true).fetchSemanticsNodes().isNotEmpty()) { "\"$text\" isn't shown" } +} + +/** Fails the scene if an element shows [text]: a screen a tap should have taken away. */ +fun ComposeUiTest.expectNoText(text: String) { + check(onAllNodes(hasText(text, substring = true), useUnmergedTree = true).fetchSemanticsNodes().isEmpty()) { "\"$text\" is still shown" } +} + /** Scrolls [text] into view in its list or strip (a tab off the strip's edge), then taps it. */ fun ComposeUiTest.scrollToText(text: String) { runOnUiThread { node(hasText(text, substring = true)).performScrollTo() } diff --git a/tools/ui-preview/src/main/kotlin/preview/Scenes.kt b/tools/ui-preview/src/main/kotlin/preview/Scenes.kt index a3eb7596..36e87932 100644 --- a/tools/ui-preview/src/main/kotlin/preview/Scenes.kt +++ b/tools/ui-preview/src/main/kotlin/preview/Scenes.kt @@ -885,6 +885,11 @@ fun scenes(): List = listOf( Scene("wide-empty", widthDp = 1280, heightDp = 800, setup = { home() }), Scene("wide", widthDp = 1280, heightDp = 800, setup = { home() }, actions = { tapText("Speed up /jobs") }), Scene("wide-terminal", route = Routes.terminal(DEVBOX, APP_SHELL), widthDp = 840, heightDp = 940), + // A space tapped in the sidebar shows in the Chat tab, also from a screen the tab had open (issue #101: the + // terminal opened from herdr-android's chat stayed, though website was marked as picked). + Scene("wide-pick", widthDp = 1280, heightDp = 800, setup = { home() }, actions = { + tapLabel("Chat"); tapText("Terminal"); tapText("website"); expectText("Rewrite the install page"); expectNoText("146×42") + }), Scene("wide-machines", widthDp = 1280, heightDp = 800, setup = { home() }, actions = { tapLabel("Chat") }), // Rotating across the line keeps what is open as it was: a chat with its key row shown, turned from a phone // to a tablet's width and from the tablet back to the phone.