-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP Sketching Compose-based workflows.
- Loading branch information
1 parent
56e6ee0
commit c007b73
Showing
7 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
workflow-core/src/commonMain/kotlin/com/squareup/workflow1/ComposeWorkflow.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.squareup.workflow1 | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.Stable | ||
import androidx.compose.runtime.remember | ||
|
||
/** | ||
* A [Workflow]-like interface that participates in a workflow tree via its [Rendering] composable. | ||
*/ | ||
@Stable | ||
public interface ComposeWorkflow< | ||
in PropsT, | ||
out OutputT, | ||
out RenderingT | ||
> { | ||
|
||
/** | ||
* The main composable of this workflow that consumes some [props] from its parent and may emit | ||
* an output via [emitOutput]. | ||
* | ||
* Equivalent to [StatefulWorkflow.render]. | ||
*/ | ||
@WorkflowComposable | ||
@Composable | ||
fun Rendering( | ||
props: PropsT, | ||
emitOutput: (OutputT) -> Unit | ||
): RenderingT | ||
} | ||
|
||
fun < | ||
PropsT, StateT, OutputT, | ||
ChildPropsT, ChildOutputT, ChildRenderingT | ||
> BaseRenderContext<PropsT, StateT, OutputT>.renderChild( | ||
child: ComposeWorkflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
key: String = "", | ||
handler: (ChildOutputT) -> WorkflowAction<PropsT, StateT, OutputT> | ||
): ChildRenderingT = renderComposable(key = key) { | ||
// Explicitly remember the output function since we know that actionSink is stable even though | ||
// Compose might not know that. | ||
val emitOutput: (ChildOutputT) -> Unit = remember(actionSink) { | ||
{ output -> | ||
val action = handler(output) | ||
actionSink.send(action) | ||
} | ||
} | ||
child.Rendering( | ||
props = props, | ||
emitOutput = emitOutput | ||
) | ||
} |
42 changes: 42 additions & 0 deletions
42
workflow-core/src/commonMain/kotlin/com/squareup/workflow1/UnitApplier.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.squareup.workflow1 | ||
|
||
import androidx.compose.runtime.Applier | ||
|
||
internal object UnitApplier : Applier<Unit> { | ||
override val current: Unit | ||
get() = Unit | ||
|
||
override fun clear() { | ||
} | ||
|
||
override fun down(node: Unit) { | ||
} | ||
|
||
override fun insertBottomUp( | ||
index: Int, | ||
instance: Unit | ||
) { | ||
} | ||
|
||
override fun insertTopDown( | ||
index: Int, | ||
instance: Unit | ||
) { | ||
} | ||
|
||
override fun move( | ||
from: Int, | ||
to: Int, | ||
count: Int | ||
) { | ||
} | ||
|
||
override fun remove( | ||
index: Int, | ||
count: Int | ||
) { | ||
} | ||
|
||
override fun up() { | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
workflow-core/src/commonMain/kotlin/com/squareup/workflow1/WorkflowComposable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.squareup.workflow1 | ||
|
||
import androidx.compose.runtime.ComposableTargetMarker | ||
import kotlin.annotation.AnnotationRetention.BINARY | ||
import kotlin.annotation.AnnotationTarget.FILE | ||
import kotlin.annotation.AnnotationTarget.FUNCTION | ||
import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER | ||
import kotlin.annotation.AnnotationTarget.TYPE | ||
import kotlin.annotation.AnnotationTarget.TYPE_PARAMETER | ||
|
||
/** | ||
* An annotation that can be used to mark a composable function as being expected to be use in a | ||
* composable function that is also marked or inferred to be marked as a [WorkflowComposable], i.e. | ||
* that can be called from [BaseRenderContext.renderComposable]. | ||
* | ||
* Using this annotation explicitly is rarely necessary as the Compose compiler plugin will infer | ||
* the necessary equivalent annotations automatically. See | ||
* [androidx.compose.runtime.ComposableTarget] for details. | ||
*/ | ||
@ComposableTargetMarker(description = "Workflow Composable") | ||
@Target(FILE, FUNCTION, PROPERTY_GETTER, TYPE, TYPE_PARAMETER) | ||
@Retention(BINARY) | ||
annotation class WorkflowComposable |
68 changes: 68 additions & 0 deletions
68
workflow-core/src/commonMain/kotlin/com/squareup/workflow1/WorkflowComposables.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.squareup.workflow1 | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.MutableState | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.staticCompositionLocalOf | ||
|
||
internal val LocalWorkflowComposableRenderer = | ||
staticCompositionLocalOf<WorkflowComposableRenderer> { error("No renderer") } | ||
|
||
internal interface WorkflowComposableRenderer { | ||
@Composable | ||
fun <ChildPropsT, ChildOutputT, ChildRenderingT> Child( | ||
workflow: Workflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
onOutput: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT | ||
} | ||
|
||
/** | ||
* Renders a child [Workflow] from any [WorkflowComposable] (e.g. a [ComposeWorkflow.Rendering] or | ||
* [BaseRenderContext.renderComposable]) and returns its rendering. | ||
* | ||
* @param handler An optional function that, if non-null, will be called when the child emits an | ||
* output. If null, the child's outputs will be ignored. | ||
*/ | ||
@WorkflowComposable | ||
@Composable | ||
fun <ChildPropsT, ChildOutputT, ChildRenderingT> Child( | ||
workflow: Workflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
onOutput: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT { | ||
val renderer = LocalWorkflowComposableRenderer.current | ||
return renderer.Child(workflow, props, onOutput) | ||
} | ||
|
||
@WorkflowComposable | ||
@Composable | ||
fun <ChildPropsT, ChildOutputT, ChildRenderingT> Child( | ||
workflow: ComposeWorkflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
handler: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT { | ||
val childRendering = remember { mutableStateOf<ChildRenderingT?>(null) } | ||
// Since this function returns a value, it can't restart without also restarting its parent. | ||
// IsolateRecomposeScope allows the subtree to restart and only restarts us if the rendering value | ||
// actually changed. | ||
IsolateRecomposeScope( | ||
child = workflow, | ||
props = props, | ||
handler = handler, | ||
result = childRendering | ||
) | ||
@Suppress("UNCHECKED_CAST") | ||
return childRendering.value as ChildRenderingT | ||
} | ||
|
||
@Composable | ||
private fun <PropsT, OutputT, RenderingT> IsolateRecomposeScope( | ||
child: ComposeWorkflow<PropsT, OutputT, RenderingT>, | ||
props: PropsT, | ||
handler: ((OutputT) -> Unit)?, | ||
result: MutableState<RenderingT>, | ||
) { | ||
result.value = child.Rendering(props, handler ?: {}) | ||
} |