From c6cc692aff8b0fe14a89bb68f46d8cdbea26aa39 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 20:43:41 -0400 Subject: [PATCH 1/8] Add basic tests for SlotBasedAdaptiveNavigationState --- library/adaptive/build.gradle.kts | 6 +- .../adaptive/AdaptiveNavigationState.kt | 10 +- .../SlotBasedAdaptiveNavigationState.kt | 6 +- .../SlotBasedAdaptiveNavigationStateTest.kt | 139 ++++++++++++++++++ 4 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt diff --git a/library/adaptive/build.gradle.kts b/library/adaptive/build.gradle.kts index 13135af..1ba0099 100644 --- a/library/adaptive/build.gradle.kts +++ b/library/adaptive/build.gradle.kts @@ -36,7 +36,6 @@ kotlin { implementation(libs.jetbrains.compose.runtime) implementation(libs.jetbrains.compose.foundation) implementation(libs.jetbrains.compose.foundation.layout) -// implementation(libs.jetbrains.compose.material3.adaptive) implementation(libs.jetbrains.lifecycle.runtime) implementation(libs.jetbrains.lifecycle.runtime.compose) @@ -44,6 +43,11 @@ kotlin { implementation(libs.jetbrains.lifecycle.viewmodel.compose) } } + commonTest { + dependencies { + implementation(kotlin("test")) + } + } val jvmMain by getting val jvmTest by getting diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt index dbb3824..11b1c78 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt @@ -13,15 +13,21 @@ interface AdaptiveNavigationState { fun adaptationIn( pane: Pane, - ): Adaptation? + ): Adaptation } /** * A description of the process that the layout undertook to adapt to its new configuration. */ sealed class Adaptation { + + /** + * Destinations remained the same in the pane + */ + data object Same : Adaptation() + /** - * Destinations were changed in panes + * Destinations were changed in the pane */ data object Change : Adaptation() diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt index cdb1367..2f1f22a 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt @@ -108,7 +108,11 @@ internal data class SlotBasedAdaptiveNavigationState( override fun adaptationIn( pane: Pane - ): Adaptation? = swapAdaptations.firstOrNull { pane in it } + ): Adaptation = + swapAdaptations.firstOrNull { pane in it } ?: when (panesToDestinations[pane]?.id) { + previousPanesToDestinations[pane]?.id -> Adaptation.Same + else -> Adaptation.Change + } } /** diff --git a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt new file mode 100644 index 0000000..209290d --- /dev/null +++ b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.tunjid.treenav.adaptive + +import com.tunjid.treenav.* +import com.tunjid.treenav.adaptive.threepane.ThreePane +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +data class TestNode(val name: String) : Node { + override val id: String get() = name +} + +class SlotBasedAdaptiveNavigationStateTest { + + private lateinit var subject: SlotBasedAdaptiveNavigationState + private lateinit var panes: List + private lateinit var slots: Set + + + @BeforeTest + fun setup() { + panes = ThreePane.entries.toList() + slots = List(size = panes.size, init = ::Slot).toSet() + subject = SlotBasedAdaptiveNavigationState.initial( + slots = slots + ) + } + + @Test + fun testFirstSinglePaneAdaptation() { + val adapted = subject.adaptTo( + slots = slots, + backStackIds = emptySet(), + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ) + ) + assertEquals( + expected = adapted.destinationFor(ThreePane.Primary), + actual = TestNode(name = "A"), + ) + assertEquals( + expected = adapted.adaptationIn(ThreePane.Primary), + actual = Adaptation.Change, + ) + assertEquals( + expected = adapted.slotFor(ThreePane.Primary), + actual = Slot(0), + ) + } + + @Test + fun testFirstTriplePaneAdaptation() { + val adapted = subject.adaptTo( + slots = slots, + backStackIds = emptySet(), + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ThreePane.Tertiary to TestNode(name = "C"), + ) + ) + // Primary + assertEquals( + expected = adapted.destinationFor(ThreePane.Primary), + actual = TestNode(name = "A") + ) + assertEquals( + expected = adapted.adaptationIn(ThreePane.Primary), + actual = Adaptation.Change + ) + assertEquals( + expected = adapted.slotFor(ThreePane.Primary), + actual = Slot(0) + ) + + // Secondary + assertEquals( + expected = adapted.destinationFor(ThreePane.Secondary), + actual = TestNode(name = "B") + ) + assertEquals( + expected = adapted.adaptationIn(ThreePane.Secondary), + actual = Adaptation.Change + ) + assertEquals( + expected = adapted.slotFor(ThreePane.Secondary), + actual = Slot(1) + ) + + // Tertiary + assertEquals( + expected = adapted.destinationFor(ThreePane.Tertiary), + actual = TestNode(name = "C") + ) + assertEquals( + expected = adapted.adaptationIn(ThreePane.Tertiary), + actual = Adaptation.Change + ) + assertEquals( + expected = adapted.slotFor(ThreePane.Tertiary), + actual = Slot(2) + ) + } + +} From fe6871386aa8f008b337d28db65b65db3e0f3e00 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 20:58:10 -0400 Subject: [PATCH 2/8] Test proper mapping of adaptations --- .../SlotBasedAdaptiveNavigationState.kt | 2 +- .../SlotBasedAdaptiveNavigationStateTest.kt | 43 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt index 2f1f22a..3ac96d3 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt @@ -182,7 +182,7 @@ internal fun SlotBasedAdaptiveNavigationState.adaptTo( panesToNodes.mapValues { it.value?.id } -> previous.swapAdaptations else -> swapAdaptations }, - previousPanesToDestinations = previous.previousPanesToDestinations.keys.associateWith( + previousPanesToDestinations = previous.panesToDestinations.keys.associateWith( valueSelector = previous::destinationFor ), destinationIdsToAdaptiveSlots = nodeIdsToAdaptiveSlots, diff --git a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt index 209290d..b40822b 100644 --- a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt +++ b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt @@ -61,9 +61,7 @@ class SlotBasedAdaptiveNavigationStateTest { @Test fun testFirstSinglePaneAdaptation() { - val adapted = subject.adaptTo( - slots = slots, - backStackIds = emptySet(), + val adapted = subject.testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), ) @@ -84,9 +82,7 @@ class SlotBasedAdaptiveNavigationStateTest { @Test fun testFirstTriplePaneAdaptation() { - val adapted = subject.adaptTo( - slots = slots, - backStackIds = emptySet(), + val adapted = subject.testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), ThreePane.Secondary to TestNode(name = "B"), @@ -136,4 +132,39 @@ class SlotBasedAdaptiveNavigationStateTest { ) } + @Test + fun testSameAdaptationInSinglePane() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ) + ) + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = Adaptation.Same, + actual = adapted.adaptationIn(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = adapted.slotFor(ThreePane.Primary), + ) + } + + private fun SlotBasedAdaptiveNavigationState.testAdaptTo( + panesToNodes: Map + ) = adaptTo( + slots = slots, + backStackIds = emptySet(), + panesToNodes = panesToNodes + ) } + From 37af480247d96d5dcb791606699c7e8180efc904 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 21:08:31 -0400 Subject: [PATCH 3/8] Add list detail tests --- .../SlotBasedAdaptiveNavigationStateTest.kt | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt index b40822b..e41bdc8 100644 --- a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt +++ b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt @@ -159,6 +159,173 @@ class SlotBasedAdaptiveNavigationStateTest { ) } + @Test + fun testSameAdaptationInThreePanes() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ThreePane.Tertiary to TestNode(name = "C"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ThreePane.Tertiary to TestNode(name = "C"), + ) + ) + // Primary + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = Adaptation.Same, + actual = adapted.adaptationIn(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = adapted.slotFor(ThreePane.Primary), + ) + + // Secondary + assertEquals( + expected = TestNode(name = "B"), + actual = adapted.destinationFor(ThreePane.Secondary), + ) + assertEquals( + expected = Adaptation.Same, + actual = adapted.adaptationIn(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(1), + actual = adapted.slotFor(ThreePane.Secondary), + ) + + // Tertiary + assertEquals( + expected = TestNode(name = "C"), + actual = adapted.destinationFor(ThreePane.Tertiary), + ) + assertEquals( + expected = Adaptation.Same, + actual = adapted.adaptationIn(ThreePane.Tertiary), + ) + assertEquals( + expected = Slot(2), + actual = adapted.slotFor(ThreePane.Tertiary), + ) + } + + @Test + fun testChangeAdaptationInThreePanes() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ThreePane.Tertiary to TestNode(name = "C"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "B"), + ThreePane.Secondary to TestNode(name = "C"), + ThreePane.Tertiary to TestNode(name = "A"), + ) + ) + // Primary + assertEquals( + expected = TestNode(name = "B"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = Adaptation.Change, + actual = adapted.adaptationIn(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = adapted.slotFor(ThreePane.Primary), + ) + + // Secondary + assertEquals( + expected = TestNode(name = "C"), + actual = adapted.destinationFor(ThreePane.Secondary), + ) + assertEquals( + expected = Adaptation.Change, + actual = adapted.adaptationIn(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(1), + actual = adapted.slotFor(ThreePane.Secondary), + ) + + // Tertiary + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.Tertiary), + ) + assertEquals( + expected = Adaptation.Change, + actual = adapted.adaptationIn(ThreePane.Tertiary), + ) + assertEquals( + expected = Slot(2), + actual = adapted.slotFor(ThreePane.Tertiary), + ) + } + + @Test + fun testListToListDetailAdaptation() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "B"), + ThreePane.Secondary to TestNode(name = "A"), + ) + ) + + // Destination assertions + assertEquals( + expected = TestNode(name = "B"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.Secondary), + ) + + // Adaptation assertions + assertEquals( + expected = Adaptation.Change, + actual = adapted.adaptationIn(ThreePane.Primary), + ) + assertEquals( + expected = ThreePane.PrimaryToSecondary, + actual = adapted.adaptationIn(ThreePane.Secondary), + ) + + // Slot assertions + assertEquals( + // Secondary should reuse slot 0 + expected = Slot(0), + actual = adapted.slotFor(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(1), + actual = adapted.slotFor(ThreePane.Primary), + ) + } + private fun SlotBasedAdaptiveNavigationState.testAdaptTo( panesToNodes: Map ) = adaptTo( From 56c614ae527d25c9e5864f0589ca562533598357 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 21:23:55 -0400 Subject: [PATCH 4/8] Add predictive back tests --- .../SlotBasedAdaptiveNavigationStateTest.kt | 107 +++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt index e41bdc8..aeab19d 100644 --- a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt +++ b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt @@ -16,12 +16,11 @@ package com.tunjid.treenav.adaptive -import com.tunjid.treenav.* +import com.tunjid.treenav.Node import com.tunjid.treenav.adaptive.threepane.ThreePane import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue /* * Copyright 2021 Google LLC @@ -326,6 +325,110 @@ class SlotBasedAdaptiveNavigationStateTest { ) } + @Test + fun testSinglePanePredictiveBackAdaptation() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "B"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.TransientPrimary to TestNode(name = "B"), + ) + ) + + // Destination assertions + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "B"), + actual = adapted.destinationFor(ThreePane.Secondary), + ) + + // Adaptation assertions + assertEquals( + expected = Adaptation.Change, + actual = adapted.adaptationIn(ThreePane.Primary), + ) + assertEquals( + expected = ThreePane.PrimaryToTransient, + actual = adapted.adaptationIn(ThreePane.TransientPrimary), + ) + + // Slot assertions + assertEquals( + // Secondary should reuse slot 0 + expected = Slot(0), + actual = adapted.slotFor(ThreePane.Primary), + ) + assertEquals( + expected = Slot(1), + actual = adapted.slotFor(ThreePane.TransientPrimary), + ) + } + + @Test + fun testDoublePaneToSinglePanePredictiveBackAdaptation() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "C"), + ThreePane.TransientPrimary to TestNode(name = "A"), + ) + ) + + // Destination assertions + assertEquals( + expected = TestNode(name = "C"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.TransientPrimary), + ) + + // Adaptation assertions + assertEquals( + expected = Adaptation.Change, + actual = adapted.adaptationIn(ThreePane.Primary), + ) + assertEquals( + expected = ThreePane.PrimaryToTransient, + actual = adapted.adaptationIn(ThreePane.TransientPrimary), + ) + + // Slot assertions + assertEquals( + // Secondary should reuse slot 0 + expected = Slot(0), + actual = adapted.slotFor(ThreePane.Primary), + ) + assertEquals( + expected = Slot(1), + actual = adapted.slotFor(ThreePane.TransientPrimary), + ) + assertEquals( + expected = null, + actual = adapted.slotFor(ThreePane.Secondary), + ) + } + private fun SlotBasedAdaptiveNavigationState.testAdaptTo( panesToNodes: Map ) = adaptTo( From a17ec37f419cd9eadc820540690d8e21a0cc079c Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 22:35:49 -0400 Subject: [PATCH 5/8] Refactor adaptations to be a set not a single value --- .../adaptive/AdaptiveNavigationState.kt | 4 +- .../treenav/adaptive/AdaptivePaneScope.kt | 4 +- .../SavedStateAdaptiveNavHostState.kt | 8 +- .../SlotBasedAdaptiveNavigationState.kt | 19 +- .../treenav/adaptive/threepane/ThreePane.kt | 4 +- .../MovableSharedElementConfiguration.kt | 2 +- .../SlotBasedAdaptiveNavigationStateTest.kt | 175 +++++++++++++----- 7 files changed, 153 insertions(+), 63 deletions(-) diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt index 11b1c78..5368b1d 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt @@ -11,9 +11,9 @@ interface AdaptiveNavigationState { pane: Pane, ): Destination? - fun adaptationIn( + fun adaptationsIn( pane: Pane, - ): Adaptation + ): Set } /** diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt index 6d941e2..b4ef94e 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt @@ -76,7 +76,7 @@ internal class AnimatedAdaptivePaneScope( sealed interface AdaptivePaneState { val currentDestination: Destination? val pane: Pane? - val adaptation: Adaptation + val adaptations: Set } /** @@ -87,7 +87,7 @@ internal data class SlotPaneState( val previousDestination: Destination?, override val currentDestination: Destination?, override val pane: Pane?, - override val adaptation: Adaptation, + override val adaptations: Set, ) : AdaptivePaneState /** diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt index 6797ba2..51ca896 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt @@ -54,9 +54,9 @@ interface AdaptiveNavHostScope { pane: Pane ) - fun adaptationIn( + fun adaptationsIn( pane: Pane, - ): Adaptation? + ): Set fun nodeFor( pane: Pane, @@ -145,9 +145,9 @@ class SavedStateAdaptiveNavHostState( slotsToRoutes[slot]?.invoke() } - override fun adaptationIn( + override fun adaptationsIn( pane: Pane - ): Adaptation? = adaptiveNavigationState.adaptationIn(pane) + ): Set = adaptiveNavigationState.adaptationsIn(pane) override fun nodeFor( pane: Pane diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt index 3ac96d3..9f6581e 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt @@ -75,8 +75,7 @@ internal data class SlotBasedAdaptiveNavigationState( currentDestination = node, previousDestination = previousPanesToDestinations[pane], pane = pane, - adaptation = swapAdaptations.firstOrNull { pane in it } - ?: Adaptation.Change, + adaptations = pane?.let(::adaptationsIn) ?: emptySet(), ) } @@ -106,13 +105,17 @@ internal data class SlotBasedAdaptiveNavigationState( pane: Pane ): Destination? = panesToDestinations[pane] - override fun adaptationIn( + override fun adaptationsIn( pane: Pane - ): Adaptation = - swapAdaptations.firstOrNull { pane in it } ?: when (panesToDestinations[pane]?.id) { - previousPanesToDestinations[pane]?.id -> Adaptation.Same - else -> Adaptation.Change - } + ): Set = + swapAdaptations.filter { pane in it } + .let { + if (it.isEmpty()) when (panesToDestinations[pane]?.id) { + previousPanesToDestinations[pane]?.id -> setOf(Adaptation.Same) + else -> setOf(Adaptation.Change) + } + else it.toSet() + } } /** diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt index 508f0f8..0d71440 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt @@ -62,14 +62,14 @@ fun threePaneAdaptiveNodeConfiguration( val state = paneState when (state.pane) { ThreePane.Primary, - ThreePane.Secondary -> when (state.adaptation) { + ThreePane.Secondary -> when (state.adaptations) { ThreePane.PrimaryToSecondary, ThreePane.SecondaryToPrimary -> NoTransition else -> DefaultTransition } - ThreePane.TransientPrimary -> when (state.adaptation) { + ThreePane.TransientPrimary -> when (state.adaptations) { ThreePane.PrimaryToTransient -> when (state.pane) { ThreePane.Secondary -> DefaultTransition else -> NoTransition diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt index 7b3a5c4..cc8a1a0 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt @@ -120,4 +120,4 @@ fun AdaptivePaneState?.canAnimateOnStartingFrames() = private val AdaptivePaneScope.isPreviewingBack: Boolean get() = paneState.pane == ThreePane.Primary - && paneState.adaptation == ThreePane.PrimaryToTransient \ No newline at end of file + && paneState.adaptations == ThreePane.PrimaryToTransient \ No newline at end of file diff --git a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt index aeab19d..48c97b2 100644 --- a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt +++ b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt @@ -70,8 +70,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = TestNode(name = "A"), ) assertEquals( - expected = adapted.adaptationIn(ThreePane.Primary), - actual = Adaptation.Change, + expected = adapted.adaptationsIn(ThreePane.Primary), + actual = setOf(Adaptation.Change), ) assertEquals( expected = adapted.slotFor(ThreePane.Primary), @@ -94,8 +94,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = TestNode(name = "A") ) assertEquals( - expected = adapted.adaptationIn(ThreePane.Primary), - actual = Adaptation.Change + expected = adapted.adaptationsIn(ThreePane.Primary), + actual = setOf(Adaptation.Change) ) assertEquals( expected = adapted.slotFor(ThreePane.Primary), @@ -108,8 +108,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = TestNode(name = "B") ) assertEquals( - expected = adapted.adaptationIn(ThreePane.Secondary), - actual = Adaptation.Change + expected = adapted.adaptationsIn(ThreePane.Secondary), + actual = setOf(Adaptation.Change) ) assertEquals( expected = adapted.slotFor(ThreePane.Secondary), @@ -122,8 +122,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = TestNode(name = "C") ) assertEquals( - expected = adapted.adaptationIn(ThreePane.Tertiary), - actual = Adaptation.Change + expected = adapted.adaptationsIn(ThreePane.Tertiary), + actual = setOf(Adaptation.Change) ) assertEquals( expected = adapted.slotFor(ThreePane.Tertiary), @@ -149,8 +149,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Primary), ) assertEquals( - expected = Adaptation.Same, - actual = adapted.adaptationIn(ThreePane.Primary), + expected = setOf(Adaptation.Same), + actual = adapted.adaptationsIn(ThreePane.Primary), ) assertEquals( expected = Slot(0), @@ -181,8 +181,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Primary), ) assertEquals( - expected = Adaptation.Same, - actual = adapted.adaptationIn(ThreePane.Primary), + expected = setOf(Adaptation.Same), + actual = adapted.adaptationsIn(ThreePane.Primary), ) assertEquals( expected = Slot(0), @@ -195,8 +195,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Secondary), ) assertEquals( - expected = Adaptation.Same, - actual = adapted.adaptationIn(ThreePane.Secondary), + expected = setOf(Adaptation.Same), + actual = adapted.adaptationsIn(ThreePane.Secondary), ) assertEquals( expected = Slot(1), @@ -209,8 +209,8 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Tertiary), ) assertEquals( - expected = Adaptation.Same, - actual = adapted.adaptationIn(ThreePane.Tertiary), + expected = setOf(Adaptation.Same), + actual = adapted.adaptationsIn(ThreePane.Tertiary), ) assertEquals( expected = Slot(2), @@ -241,11 +241,14 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Primary), ) assertEquals( - expected = Adaptation.Change, - actual = adapted.adaptationIn(ThreePane.Primary), + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Tertiary), + Adaptation.Swap(from = ThreePane.Secondary, to = ThreePane.Primary), + ), + actual = adapted.adaptationsIn(ThreePane.Primary), ) assertEquals( - expected = Slot(0), + expected = Slot(1), actual = adapted.slotFor(ThreePane.Primary), ) @@ -255,11 +258,14 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Secondary), ) assertEquals( - expected = Adaptation.Change, - actual = adapted.adaptationIn(ThreePane.Secondary), + expected = setOf( + Adaptation.Swap(from = ThreePane.Tertiary, to = ThreePane.Secondary), + Adaptation.Swap(from = ThreePane.Secondary, to = ThreePane.Primary), + ), + actual = adapted.adaptationsIn(ThreePane.Secondary), ) assertEquals( - expected = Slot(1), + expected = Slot(2), actual = adapted.slotFor(ThreePane.Secondary), ) @@ -269,11 +275,14 @@ class SlotBasedAdaptiveNavigationStateTest { actual = adapted.destinationFor(ThreePane.Tertiary), ) assertEquals( - expected = Adaptation.Change, - actual = adapted.adaptationIn(ThreePane.Tertiary), + expected = setOf( + Adaptation.Swap(from = ThreePane.Tertiary, to = ThreePane.Secondary), + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Tertiary), + ), + actual = adapted.adaptationsIn(ThreePane.Tertiary), ) assertEquals( - expected = Slot(2), + expected = Slot(0), actual = adapted.slotFor(ThreePane.Tertiary), ) } @@ -305,12 +314,14 @@ class SlotBasedAdaptiveNavigationStateTest { // Adaptation assertions assertEquals( - expected = Adaptation.Change, - actual = adapted.adaptationIn(ThreePane.Primary), + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Secondary), + ), + actual = adapted.adaptationsIn(ThreePane.Primary), ) assertEquals( - expected = ThreePane.PrimaryToSecondary, - actual = adapted.adaptationIn(ThreePane.Secondary), + expected = setOf(ThreePane.PrimaryToSecondary), + actual = adapted.adaptationsIn(ThreePane.Secondary), ) // Slot assertions @@ -333,11 +344,23 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.Primary to TestNode(name = "A"), ) ) + .apply { + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.Primary), + ) + } .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "B"), ) ) + .apply { + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.Primary), + ) + } .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -352,27 +375,26 @@ class SlotBasedAdaptiveNavigationStateTest { ) assertEquals( expected = TestNode(name = "B"), - actual = adapted.destinationFor(ThreePane.Secondary), + actual = adapted.destinationFor(ThreePane.TransientPrimary), ) // Adaptation assertions assertEquals( - expected = Adaptation.Change, - actual = adapted.adaptationIn(ThreePane.Primary), + expected = setOf(ThreePane.PrimaryToTransient), + actual = adapted.adaptationsIn(ThreePane.Primary), ) assertEquals( - expected = ThreePane.PrimaryToTransient, - actual = adapted.adaptationIn(ThreePane.TransientPrimary), + expected = setOf(ThreePane.PrimaryToTransient), + actual = adapted.adaptationsIn(ThreePane.TransientPrimary), ) // Slot assertions assertEquals( - // Secondary should reuse slot 0 - expected = Slot(0), + expected = Slot(1), actual = adapted.slotFor(ThreePane.Primary), ) assertEquals( - expected = Slot(1), + expected = Slot(0), actual = adapted.slotFor(ThreePane.TransientPrimary), ) } @@ -405,22 +427,23 @@ class SlotBasedAdaptiveNavigationStateTest { // Adaptation assertions assertEquals( - expected = Adaptation.Change, - actual = adapted.adaptationIn(ThreePane.Primary), + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary), + ), + actual = adapted.adaptationsIn(ThreePane.Primary), ) assertEquals( - expected = ThreePane.PrimaryToTransient, - actual = adapted.adaptationIn(ThreePane.TransientPrimary), + expected = setOf(ThreePane.PrimaryToTransient), + actual = adapted.adaptationsIn(ThreePane.TransientPrimary), ) // Slot assertions assertEquals( - // Secondary should reuse slot 0 - expected = Slot(0), + expected = Slot(1), actual = adapted.slotFor(ThreePane.Primary), ) assertEquals( - expected = Slot(1), + expected = Slot(0), actual = adapted.slotFor(ThreePane.TransientPrimary), ) assertEquals( @@ -429,6 +452,70 @@ class SlotBasedAdaptiveNavigationStateTest { ) } + @Test + fun testDoublePaneToDoublePanePredictiveBackAdaptation() { + val adapted = subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ) + ) + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "C"), + ThreePane.Secondary to TestNode(name = "D"), + ThreePane.TransientPrimary to TestNode(name = "A"), + ) + ) + + // Destination assertions + assertEquals( + expected = TestNode(name = "C"), + actual = adapted.destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "D"), + actual = adapted.destinationFor(ThreePane.Secondary), + ) + assertEquals( + expected = TestNode(name = "A"), + actual = adapted.destinationFor(ThreePane.TransientPrimary), + ) + + // Adaptation assertions + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary) + ), + actual = adapted.adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = setOf(Adaptation.Change), + actual = adapted.adaptationsIn(ThreePane.Secondary), + ) + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary) + ), + actual = adapted.adaptationsIn(ThreePane.TransientPrimary), + ) + + // Slot assertions + assertEquals( + expected = Slot(1), + actual = adapted.slotFor(ThreePane.Primary), + ) + assertEquals( + expected = Slot(2), + actual = adapted.slotFor(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(0), + actual = adapted.slotFor(ThreePane.TransientPrimary), + ) + } + private fun SlotBasedAdaptiveNavigationState.testAdaptTo( panesToNodes: Map ) = adaptTo( From a22ca2e929113932a1da80b4bf1816fe929e8df0 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 22:39:59 -0400 Subject: [PATCH 6/8] Clean up tests --- .../SlotBasedAdaptiveNavigationStateTest.kt | 617 +++++++++--------- 1 file changed, 316 insertions(+), 301 deletions(-) diff --git a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt index 48c97b2..d395002 100644 --- a/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt +++ b/library/adaptive/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt @@ -60,80 +60,85 @@ class SlotBasedAdaptiveNavigationStateTest { @Test fun testFirstSinglePaneAdaptation() { - val adapted = subject.testAdaptTo( + subject.testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), ) ) - assertEquals( - expected = adapted.destinationFor(ThreePane.Primary), - actual = TestNode(name = "A"), - ) - assertEquals( - expected = adapted.adaptationsIn(ThreePane.Primary), - actual = setOf(Adaptation.Change), - ) - assertEquals( - expected = adapted.slotFor(ThreePane.Primary), - actual = Slot(0), - ) + .apply { + assertEquals( + expected = destinationFor(ThreePane.Primary), + actual = TestNode(name = "A"), + ) + assertEquals( + expected = adaptationsIn(ThreePane.Primary), + actual = setOf(Adaptation.Change), + ) + assertEquals( + expected = slotFor(ThreePane.Primary), + actual = Slot(0), + ) + } } @Test fun testFirstTriplePaneAdaptation() { - val adapted = subject.testAdaptTo( - panesToNodes = mapOf( - ThreePane.Primary to TestNode(name = "A"), - ThreePane.Secondary to TestNode(name = "B"), - ThreePane.Tertiary to TestNode(name = "C"), + subject + .testAdaptTo( + panesToNodes = mapOf( + ThreePane.Primary to TestNode(name = "A"), + ThreePane.Secondary to TestNode(name = "B"), + ThreePane.Tertiary to TestNode(name = "C"), + ) ) - ) - // Primary - assertEquals( - expected = adapted.destinationFor(ThreePane.Primary), - actual = TestNode(name = "A") - ) - assertEquals( - expected = adapted.adaptationsIn(ThreePane.Primary), - actual = setOf(Adaptation.Change) - ) - assertEquals( - expected = adapted.slotFor(ThreePane.Primary), - actual = Slot(0) - ) + .apply { + // Primary + assertEquals( + expected = destinationFor(ThreePane.Primary), + actual = TestNode(name = "A") + ) + assertEquals( + expected = adaptationsIn(ThreePane.Primary), + actual = setOf(Adaptation.Change) + ) + assertEquals( + expected = slotFor(ThreePane.Primary), + actual = Slot(0) + ) - // Secondary - assertEquals( - expected = adapted.destinationFor(ThreePane.Secondary), - actual = TestNode(name = "B") - ) - assertEquals( - expected = adapted.adaptationsIn(ThreePane.Secondary), - actual = setOf(Adaptation.Change) - ) - assertEquals( - expected = adapted.slotFor(ThreePane.Secondary), - actual = Slot(1) - ) + // Secondary + assertEquals( + expected = destinationFor(ThreePane.Secondary), + actual = TestNode(name = "B") + ) + assertEquals( + expected = adaptationsIn(ThreePane.Secondary), + actual = setOf(Adaptation.Change) + ) + assertEquals( + expected = slotFor(ThreePane.Secondary), + actual = Slot(1) + ) - // Tertiary - assertEquals( - expected = adapted.destinationFor(ThreePane.Tertiary), - actual = TestNode(name = "C") - ) - assertEquals( - expected = adapted.adaptationsIn(ThreePane.Tertiary), - actual = setOf(Adaptation.Change) - ) - assertEquals( - expected = adapted.slotFor(ThreePane.Tertiary), - actual = Slot(2) - ) + // Tertiary + assertEquals( + expected = destinationFor(ThreePane.Tertiary), + actual = TestNode(name = "C") + ) + assertEquals( + expected = adaptationsIn(ThreePane.Tertiary), + actual = setOf(Adaptation.Change) + ) + assertEquals( + expected = slotFor(ThreePane.Tertiary), + actual = Slot(2) + ) + } } @Test fun testSameAdaptationInSinglePane() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -144,23 +149,25 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.Primary to TestNode(name = "A"), ) ) - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = setOf(Adaptation.Same), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = Slot(0), - actual = adapted.slotFor(ThreePane.Primary), - ) + .apply { + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = setOf(Adaptation.Same), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.Primary), + ) + } } @Test fun testSameAdaptationInThreePanes() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -175,52 +182,54 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.Tertiary to TestNode(name = "C"), ) ) - // Primary - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = setOf(Adaptation.Same), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = Slot(0), - actual = adapted.slotFor(ThreePane.Primary), - ) + .apply { + // Primary + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = setOf(Adaptation.Same), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.Primary), + ) - // Secondary - assertEquals( - expected = TestNode(name = "B"), - actual = adapted.destinationFor(ThreePane.Secondary), - ) - assertEquals( - expected = setOf(Adaptation.Same), - actual = adapted.adaptationsIn(ThreePane.Secondary), - ) - assertEquals( - expected = Slot(1), - actual = adapted.slotFor(ThreePane.Secondary), - ) + // Secondary + assertEquals( + expected = TestNode(name = "B"), + actual = destinationFor(ThreePane.Secondary), + ) + assertEquals( + expected = setOf(Adaptation.Same), + actual = adaptationsIn(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(1), + actual = slotFor(ThreePane.Secondary), + ) - // Tertiary - assertEquals( - expected = TestNode(name = "C"), - actual = adapted.destinationFor(ThreePane.Tertiary), - ) - assertEquals( - expected = setOf(Adaptation.Same), - actual = adapted.adaptationsIn(ThreePane.Tertiary), - ) - assertEquals( - expected = Slot(2), - actual = adapted.slotFor(ThreePane.Tertiary), - ) + // Tertiary + assertEquals( + expected = TestNode(name = "C"), + actual = destinationFor(ThreePane.Tertiary), + ) + assertEquals( + expected = setOf(Adaptation.Same), + actual = adaptationsIn(ThreePane.Tertiary), + ) + assertEquals( + expected = Slot(2), + actual = slotFor(ThreePane.Tertiary), + ) + } } @Test fun testChangeAdaptationInThreePanes() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -235,61 +244,63 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.Tertiary to TestNode(name = "A"), ) ) - // Primary - assertEquals( - expected = TestNode(name = "B"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Tertiary), - Adaptation.Swap(from = ThreePane.Secondary, to = ThreePane.Primary), - ), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = Slot(1), - actual = adapted.slotFor(ThreePane.Primary), - ) + .apply { + // Primary + assertEquals( + expected = TestNode(name = "B"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Tertiary), + Adaptation.Swap(from = ThreePane.Secondary, to = ThreePane.Primary), + ), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = Slot(1), + actual = slotFor(ThreePane.Primary), + ) - // Secondary - assertEquals( - expected = TestNode(name = "C"), - actual = adapted.destinationFor(ThreePane.Secondary), - ) - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Tertiary, to = ThreePane.Secondary), - Adaptation.Swap(from = ThreePane.Secondary, to = ThreePane.Primary), - ), - actual = adapted.adaptationsIn(ThreePane.Secondary), - ) - assertEquals( - expected = Slot(2), - actual = adapted.slotFor(ThreePane.Secondary), - ) + // Secondary + assertEquals( + expected = TestNode(name = "C"), + actual = destinationFor(ThreePane.Secondary), + ) + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Tertiary, to = ThreePane.Secondary), + Adaptation.Swap(from = ThreePane.Secondary, to = ThreePane.Primary), + ), + actual = adaptationsIn(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(2), + actual = slotFor(ThreePane.Secondary), + ) - // Tertiary - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.Tertiary), - ) - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Tertiary, to = ThreePane.Secondary), - Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Tertiary), - ), - actual = adapted.adaptationsIn(ThreePane.Tertiary), - ) - assertEquals( - expected = Slot(0), - actual = adapted.slotFor(ThreePane.Tertiary), - ) + // Tertiary + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.Tertiary), + ) + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Tertiary, to = ThreePane.Secondary), + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Tertiary), + ), + actual = adaptationsIn(ThreePane.Tertiary), + ) + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.Tertiary), + ) + } } @Test fun testListToListDetailAdaptation() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -301,44 +312,45 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.Secondary to TestNode(name = "A"), ) ) + .apply { + // Destination assertions + assertEquals( + expected = TestNode(name = "B"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.Secondary), + ) - // Destination assertions - assertEquals( - expected = TestNode(name = "B"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.Secondary), - ) - - // Adaptation assertions - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Secondary), - ), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = setOf(ThreePane.PrimaryToSecondary), - actual = adapted.adaptationsIn(ThreePane.Secondary), - ) + // Adaptation assertions + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.Secondary), + ), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = setOf(ThreePane.PrimaryToSecondary), + actual = adaptationsIn(ThreePane.Secondary), + ) - // Slot assertions - assertEquals( - // Secondary should reuse slot 0 - expected = Slot(0), - actual = adapted.slotFor(ThreePane.Secondary), - ) - assertEquals( - expected = Slot(1), - actual = adapted.slotFor(ThreePane.Primary), - ) + // Slot assertions + assertEquals( + // Secondary should reuse slot 0 + expected = Slot(0), + actual = slotFor(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(1), + actual = slotFor(ThreePane.Primary), + ) + } } @Test fun testSinglePanePredictiveBackAdaptation() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -367,41 +379,42 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.TransientPrimary to TestNode(name = "B"), ) ) + .apply { + // Destination assertions + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "B"), + actual = destinationFor(ThreePane.TransientPrimary), + ) - // Destination assertions - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = TestNode(name = "B"), - actual = adapted.destinationFor(ThreePane.TransientPrimary), - ) - - // Adaptation assertions - assertEquals( - expected = setOf(ThreePane.PrimaryToTransient), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = setOf(ThreePane.PrimaryToTransient), - actual = adapted.adaptationsIn(ThreePane.TransientPrimary), - ) + // Adaptation assertions + assertEquals( + expected = setOf(ThreePane.PrimaryToTransient), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = setOf(ThreePane.PrimaryToTransient), + actual = adaptationsIn(ThreePane.TransientPrimary), + ) - // Slot assertions - assertEquals( - expected = Slot(1), - actual = adapted.slotFor(ThreePane.Primary), - ) - assertEquals( - expected = Slot(0), - actual = adapted.slotFor(ThreePane.TransientPrimary), - ) + // Slot assertions + assertEquals( + expected = Slot(1), + actual = slotFor(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.TransientPrimary), + ) + } } @Test fun testDoublePaneToSinglePanePredictiveBackAdaptation() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -414,47 +427,48 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.TransientPrimary to TestNode(name = "A"), ) ) + .apply { + // Destination assertions + assertEquals( + expected = TestNode(name = "C"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.TransientPrimary), + ) - // Destination assertions - assertEquals( - expected = TestNode(name = "C"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.TransientPrimary), - ) - - // Adaptation assertions - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary), - ), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = setOf(ThreePane.PrimaryToTransient), - actual = adapted.adaptationsIn(ThreePane.TransientPrimary), - ) + // Adaptation assertions + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary), + ), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = setOf(ThreePane.PrimaryToTransient), + actual = adaptationsIn(ThreePane.TransientPrimary), + ) - // Slot assertions - assertEquals( - expected = Slot(1), - actual = adapted.slotFor(ThreePane.Primary), - ) - assertEquals( - expected = Slot(0), - actual = adapted.slotFor(ThreePane.TransientPrimary), - ) - assertEquals( - expected = null, - actual = adapted.slotFor(ThreePane.Secondary), - ) + // Slot assertions + assertEquals( + expected = Slot(1), + actual = slotFor(ThreePane.Primary), + ) + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.TransientPrimary), + ) + assertEquals( + expected = null, + actual = slotFor(ThreePane.Secondary), + ) + } } @Test fun testDoublePaneToDoublePanePredictiveBackAdaptation() { - val adapted = subject + subject .testAdaptTo( panesToNodes = mapOf( ThreePane.Primary to TestNode(name = "A"), @@ -468,52 +482,53 @@ class SlotBasedAdaptiveNavigationStateTest { ThreePane.TransientPrimary to TestNode(name = "A"), ) ) + .apply { + // Destination assertions + assertEquals( + expected = TestNode(name = "C"), + actual = destinationFor(ThreePane.Primary), + ) + assertEquals( + expected = TestNode(name = "D"), + actual = destinationFor(ThreePane.Secondary), + ) + assertEquals( + expected = TestNode(name = "A"), + actual = destinationFor(ThreePane.TransientPrimary), + ) - // Destination assertions - assertEquals( - expected = TestNode(name = "C"), - actual = adapted.destinationFor(ThreePane.Primary), - ) - assertEquals( - expected = TestNode(name = "D"), - actual = adapted.destinationFor(ThreePane.Secondary), - ) - assertEquals( - expected = TestNode(name = "A"), - actual = adapted.destinationFor(ThreePane.TransientPrimary), - ) - - // Adaptation assertions - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary) - ), - actual = adapted.adaptationsIn(ThreePane.Primary), - ) - assertEquals( - expected = setOf(Adaptation.Change), - actual = adapted.adaptationsIn(ThreePane.Secondary), - ) - assertEquals( - expected = setOf( - Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary) - ), - actual = adapted.adaptationsIn(ThreePane.TransientPrimary), - ) + // Adaptation assertions + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary) + ), + actual = adaptationsIn(ThreePane.Primary), + ) + assertEquals( + expected = setOf(Adaptation.Change), + actual = adaptationsIn(ThreePane.Secondary), + ) + assertEquals( + expected = setOf( + Adaptation.Swap(from = ThreePane.Primary, to = ThreePane.TransientPrimary) + ), + actual = adaptationsIn(ThreePane.TransientPrimary), + ) - // Slot assertions - assertEquals( - expected = Slot(1), - actual = adapted.slotFor(ThreePane.Primary), - ) - assertEquals( - expected = Slot(2), - actual = adapted.slotFor(ThreePane.Secondary), - ) - assertEquals( - expected = Slot(0), - actual = adapted.slotFor(ThreePane.TransientPrimary), - ) + // Slot assertions + assertEquals( + expected = Slot(1), + actual = slotFor(ThreePane.Primary), + ) + assertEquals( + expected = Slot(2), + actual = slotFor(ThreePane.Secondary), + ) + assertEquals( + expected = Slot(0), + actual = slotFor(ThreePane.TransientPrimary), + ) + } } private fun SlotBasedAdaptiveNavigationState.testAdaptTo( From 940bc2691824a28b7afb85ad92eb0374e5bb1ee9 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 22:49:36 -0400 Subject: [PATCH 7/8] Code formatting and kdocs for AdaptiveNavigationState --- .../treenav/adaptive/AdaptiveNavigationState.kt | 8 ++++++++ .../SlotBasedAdaptiveNavigationState.kt | 17 ++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt index 5368b1d..5c1e978 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt @@ -7,10 +7,18 @@ import com.tunjid.treenav.Node */ interface AdaptiveNavigationState { + /** + * The current [Destination] in this [pane]. + * @param pane the [Pane] to query. + */ fun destinationFor( pane: Pane, ): Destination? + /** + * Adaptations involving this [pane] after the last navigation state change. + * @param pane the affected [Pane]. + */ fun adaptationsIn( pane: Pane, ): Set diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt index 9f6581e..2a8a891 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt @@ -107,15 +107,14 @@ internal data class SlotBasedAdaptiveNavigationState( override fun adaptationsIn( pane: Pane - ): Set = - swapAdaptations.filter { pane in it } - .let { - if (it.isEmpty()) when (panesToDestinations[pane]?.id) { - previousPanesToDestinations[pane]?.id -> setOf(Adaptation.Same) - else -> setOf(Adaptation.Change) - } - else it.toSet() - } + ): Set { + val swaps = swapAdaptations.filter { pane in it } + return if (swaps.isEmpty()) when (panesToDestinations[pane]?.id) { + previousPanesToDestinations[pane]?.id -> setOf(Adaptation.Same) + else -> setOf(Adaptation.Change) + } + else swaps.toSet() + } } /** From cbe2fcf223725bbdc2a346d30e0ecd027424af33 Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Mon, 7 Oct 2024 22:51:32 -0400 Subject: [PATCH 8/8] Fix is previewing back --- .../configurations/MovableSharedElementConfiguration.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt index cc8a1a0..9dcc9fe 100644 --- a/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt +++ b/library/adaptive/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt @@ -120,4 +120,4 @@ fun AdaptivePaneState?.canAnimateOnStartingFrames() = private val AdaptivePaneScope.isPreviewingBack: Boolean get() = paneState.pane == ThreePane.Primary - && paneState.adaptations == ThreePane.PrimaryToTransient \ No newline at end of file + && paneState.adaptations.contains(ThreePane.PrimaryToTransient) \ No newline at end of file