Skip to content
This repository was archived by the owner on Feb 5, 2021. It is now read-only.

Commit f94ee4c

Browse files
Renames ComposeViewFactoryRoot to CompositionRoot and decouples the implementation.
The root is now applied via a `ViewRegistry` wrapper that wraps individual factories to apply the root, instead of having this logic hard-coded inside `ComposeViewFactory`. This approach is more flexible in general (could be used to do other tricks), and decouples the rooting feature from the rest of the code.
1 parent 240cbe7 commit f94ee4c

File tree

12 files changed

+247
-324
lines changed

12 files changed

+247
-324
lines changed

core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ComposeViewFactoryTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ComposeViewFactoryTest {
4040
@Test fun wrapsFactoryWithRoot() {
4141
val wrapperText = mutableStateOf("one")
4242
val viewEnvironment = ViewEnvironment(ViewRegistry(TestFactory))
43-
.withComposeViewFactoryRoot { content ->
43+
.withCompositionRoot { content ->
4444
Column {
4545
Text(wrapperText.value)
4646
content()
Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.squareup.workflow.ui.compose.internal
16+
package com.squareup.workflow.ui.compose
1717

18+
import androidx.compose.FrameManager
19+
import androidx.compose.mutableStateOf
1820
import androidx.test.ext.junit.runners.AndroidJUnit4
1921
import androidx.ui.foundation.Text
2022
import androidx.ui.layout.Column
@@ -23,28 +25,114 @@ import androidx.ui.test.assertIsDisplayed
2325
import androidx.ui.test.createComposeRule
2426
import androidx.ui.test.findByText
2527
import com.google.common.truth.Truth.assertThat
26-
import com.squareup.workflow.ui.compose.ComposeViewFactoryRoot
2728
import org.junit.Rule
2829
import org.junit.Test
2930
import org.junit.runner.RunWith
3031
import kotlin.test.assertFailsWith
3132

3233
@RunWith(AndroidJUnit4::class)
33-
class SafeComposeViewFactoryRootTest {
34+
class CompositionRootTest {
3435

3536
@Rule @JvmField val composeRule = createComposeRule()
3637

38+
@Test fun wrapWithRootIfNecessary_wrapsWhenNecessary() {
39+
val root: CompositionRoot = { content ->
40+
Column {
41+
Text("one")
42+
content()
43+
}
44+
}
45+
46+
composeRule.setContent {
47+
wrapWithRootIfNecessary(root) {
48+
Text("two")
49+
}
50+
}
51+
52+
findByText("one\ntwo").assertIsDisplayed()
53+
}
54+
55+
@Test fun wrapWithRootIfNecessary_onlyWrapsOnce() {
56+
val root: CompositionRoot = { content ->
57+
Column {
58+
Text("one")
59+
content()
60+
}
61+
}
62+
63+
composeRule.setContent {
64+
wrapWithRootIfNecessary(root) {
65+
Text("two")
66+
wrapWithRootIfNecessary(root) {
67+
Text("three")
68+
}
69+
}
70+
}
71+
72+
findByText("one\ntwo\nthree").assertIsDisplayed()
73+
}
74+
75+
@Test fun wrapWithRootIfNecessary_seesUpdatesFromRootWrapper() {
76+
val wrapperText = mutableStateOf("one")
77+
val root: CompositionRoot = { content ->
78+
Column {
79+
Text(wrapperText.value)
80+
content()
81+
}
82+
}
83+
84+
composeRule.setContent {
85+
wrapWithRootIfNecessary(root) {
86+
Text("two")
87+
}
88+
}
89+
90+
findByText("one\ntwo").assertIsDisplayed()
91+
FrameManager.framed {
92+
wrapperText.value = "ENO"
93+
}
94+
findByText("ENO\ntwo").assertIsDisplayed()
95+
}
96+
97+
@Test fun wrapWithRootIfNecessary_rewrapsWhenDifferentRoot() {
98+
val root1: CompositionRoot = { content ->
99+
Column {
100+
Text("one")
101+
content()
102+
}
103+
}
104+
val root2: CompositionRoot = { content ->
105+
Column {
106+
Text("ENO")
107+
content()
108+
}
109+
}
110+
val viewEnvironment = mutableStateOf(root1)
111+
112+
composeRule.setContent {
113+
wrapWithRootIfNecessary(viewEnvironment.value) {
114+
Text("two")
115+
}
116+
}
117+
118+
findByText("one\ntwo").assertIsDisplayed()
119+
FrameManager.framed {
120+
viewEnvironment.value = root2
121+
}
122+
findByText("ENO\ntwo").assertIsDisplayed()
123+
}
124+
37125
@Test fun safeComposeViewFactoryRoot_wraps_content() {
38-
val wrapped = ComposeViewFactoryRoot { content ->
126+
val wrapped: CompositionRoot = { content ->
39127
Column {
40128
Text("Parent")
41129
content()
42130
}
43131
}
44-
val safeRoot = SafeComposeViewFactoryRoot(wrapped)
132+
val safeRoot = safeCompositionRoot(wrapped)
45133

46134
composeRule.setContent {
47-
safeRoot.wrap {
135+
safeRoot {
48136
// Need an explicit semantics container, otherwise both Texts will be merged into a single
49137
// Semantics object with the text "Parent\nChild".
50138
Semantics(container = true) {
@@ -58,12 +146,12 @@ class SafeComposeViewFactoryRootTest {
58146
}
59147

60148
@Test fun safeComposeViewFactoryRoot_throws_whenChildrenNotInvoked() {
61-
val wrapped = ComposeViewFactoryRoot { }
62-
val safeRoot = SafeComposeViewFactoryRoot(wrapped)
149+
val wrapped: CompositionRoot = { }
150+
val safeRoot = safeCompositionRoot(wrapped)
63151

64152
val error = assertFailsWith<IllegalStateException> {
65153
composeRule.setContent {
66-
safeRoot.wrap {}
154+
safeRoot {}
67155
}
68156
}
69157

@@ -74,15 +162,15 @@ class SafeComposeViewFactoryRootTest {
74162
}
75163

76164
@Test fun safeComposeViewFactoryRoot_throws_whenChildrenInvokedMultipleTimes() {
77-
val wrapped = ComposeViewFactoryRoot { children ->
165+
val wrapped: CompositionRoot = { children ->
78166
children()
79167
children()
80168
}
81-
val safeRoot = SafeComposeViewFactoryRoot(wrapped)
169+
val safeRoot = safeCompositionRoot(wrapped)
82170

83171
val error = assertFailsWith<IllegalStateException> {
84172
composeRule.setContent {
85-
safeRoot.wrap {
173+
safeRoot {
86174
Text("Hello")
87175
}
88176
}

core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/internal/ComposeViewFactoryRootTest.kt

Lines changed: 0 additions & 142 deletions
This file was deleted.

core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/internal/ViewFactoriesTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import com.squareup.workflow.ui.ViewEnvironment
2525
import com.squareup.workflow.ui.ViewRegistry
2626
import com.squareup.workflow.ui.compose.bindCompose
2727
import com.squareup.workflow.ui.compose.showRendering
28-
import com.squareup.workflow.ui.compose.withComposeViewFactoryRoot
28+
import com.squareup.workflow.ui.compose.withCompositionRoot
2929
import org.junit.Rule
3030
import org.junit.Test
3131
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@ class ViewFactoriesTest {
3737

3838
@Test fun showRendering_wrapsFactoryWithRoot_whenAlreadyInComposition() {
3939
val viewEnvironment = ViewEnvironment(ViewRegistry(TestFactory))
40-
.withComposeViewFactoryRoot { content ->
40+
.withCompositionRoot { content ->
4141
Column {
4242
Text("one")
4343
content()

core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeViewFactory.kt

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,10 @@ import kotlin.reflect.KClass
7373
* ## Initializing Compose context
7474
*
7575
* Often all the [bindCompose] factories in an app need to share some context – for example, certain
76-
* ambients need to be provided, such as `MaterialTheme`. To configure this shared context, include
77-
* a [ComposeViewFactoryRoot] in your top-level [ViewEnvironment] (e.g. by using
78-
* [withComposeViewFactoryRoot]). The first time a [bindCompose] is used to show a rendering, its
79-
* [showRendering] function will be wrapped with the [ComposeViewFactoryRoot]. See the documentation
80-
* on [ComposeViewFactoryRoot] for more information.
76+
* ambients need to be provided, such as `MaterialTheme`. To configure this shared context, call
77+
* [withCompositionRoot] on your top-level [ViewEnvironment]. The first time a [bindCompose] is used
78+
* to show a rendering, its [showRendering] function will be wrapped with the [CompositionRoot].
79+
* See the documentation on [CompositionRoot] for more information.
8180
*/
8281
inline fun <reified RenderingT : Any> bindCompose(
8382
noinline showRendering: @Composable() (
@@ -89,7 +88,7 @@ inline fun <reified RenderingT : Any> bindCompose(
8988
@PublishedApi
9089
internal class ComposeViewFactory<RenderingT : Any>(
9190
override val type: KClass<RenderingT>,
92-
private val content: @Composable() (RenderingT, ViewEnvironment) -> Unit
91+
val content: @Composable() (RenderingT, ViewEnvironment) -> Unit
9392
) : ViewFactory<RenderingT> {
9493

9594
override fun buildView(
@@ -129,22 +128,9 @@ internal class ComposeViewFactory<RenderingT : Any>(
129128
// Entry point to the world of Compose.
130129
composeContainer.setOrContinueContent(initialViewEnvironment) {
131130
val (rendering, environment) = renderState.value!!
132-
showRenderingWrappedWithRoot(rendering, environment)
131+
content(rendering, environment)
133132
}
134133

135134
return composeContainer
136135
}
137-
138-
/**
139-
* Invokes [content]. If this is the highest [ComposeViewFactory] in the tree, wraps with
140-
* the [ComposeViewFactoryRoot] if present in the [ViewEnvironment].
141-
*/
142-
@Composable internal fun showRenderingWrappedWithRoot(
143-
rendering: RenderingT,
144-
viewEnvironment: ViewEnvironment
145-
) {
146-
wrapWithRootIfNecessary(viewEnvironment) {
147-
content(rendering, viewEnvironment)
148-
}
149-
}
150136
}

0 commit comments

Comments
 (0)