Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions workflow-ui/core-android/api/core-android.api
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ public final class com/squareup/workflow1/ui/LayoutScreenViewFactory : com/squar
public fun getType ()Lkotlin/reflect/KClass;
}

public final class com/squareup/workflow1/ui/NamedScreenViewFactoryKt {
public static final fun NamedScreenViewFactory ()Lcom/squareup/workflow1/ui/ScreenViewFactory;
}

public final class com/squareup/workflow1/ui/NamedViewFactory : com/squareup/workflow1/ui/ViewFactory {
public static final field INSTANCE Lcom/squareup/workflow1/ui/NamedViewFactory;
public fun buildView (Lcom/squareup/workflow1/ui/Named;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View;
Expand Down Expand Up @@ -157,6 +153,9 @@ public abstract interface class com/squareup/workflow1/ui/ScreenViewFactory : co
}

public final class com/squareup/workflow1/ui/ScreenViewFactory$Companion {
public final synthetic fun forWrapper (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public final synthetic fun forWrapper (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public static synthetic fun forWrapper$default (Lcom/squareup/workflow1/ui/ScreenViewFactory$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public final synthetic fun fromCode (Lkotlin/jvm/functions/Function4;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public final synthetic fun fromLayout (ILkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public final synthetic fun fromStaticLayout (I)Lcom/squareup/workflow1/ui/ScreenViewFactory;
Expand Down Expand Up @@ -185,7 +184,6 @@ public final class com/squareup/workflow1/ui/ScreenViewFactoryFinder$DefaultImpl
public final class com/squareup/workflow1/ui/ScreenViewFactoryKt {
public static final fun startShowing (Lcom/squareup/workflow1/ui/ScreenViewFactory;Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lcom/squareup/workflow1/ui/ViewStarter;)Lcom/squareup/workflow1/ui/ScreenViewHolder;
public static synthetic fun startShowing$default (Lcom/squareup/workflow1/ui/ScreenViewFactory;Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lcom/squareup/workflow1/ui/ViewStarter;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/ScreenViewHolder;
public static final synthetic fun toUnwrappingViewFactory (Lcom/squareup/workflow1/ui/ScreenViewFactory;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public static final synthetic fun toUnwrappingViewFactory (Lcom/squareup/workflow1/ui/ScreenViewFactory;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public static final fun toViewFactory (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/ScreenViewFactory;
public static final fun viewBindingLayoutInflater (Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/LayoutInflater;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import android.view.ViewGroup
import kotlin.reflect.KClass

/**
* **This will be deprecated in favor of [ScreenViewFactory.toUnwrappingViewFactory] very soon.**
* **This will be deprecated in favor of [ScreenViewFactory.forWrapper] very soon.**
*
* A [ViewFactory] for [OuterT] that delegates view construction responsibilities
* to the factory registered for [InnerT]. Makes it convenient for [OuterT] to wrap
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,6 @@ import com.squareup.workflow1.ui.ScreenViewFactory.Companion.fromViewBinding
@WorkflowUiExperimentalApi
public typealias ViewBindingInflater<BindingT> = (LayoutInflater, ViewGroup?, Boolean) -> BindingT

/**
* The function that updates a [View] instance built by a [ScreenViewFactory].
* Each [ScreenViewRunner] instance is paired with the single [View] instance,
* its neighbor in a [ScreenViewHolder].
*
* This is the interface you'll implement directly to update Android view code
* from your [Screen] renderings. A [ScreenViewRunner] serves as the strategy
* object of a [ScreenViewHolder] instantiated by a [ScreenViewFactory] -- the
* runner provides the implementation for the holder's [ScreenViewHolder.show]
* method.
*/
@WorkflowUiExperimentalApi
public fun interface ScreenViewRunner<in ScreenT : Screen> {
public fun showRendering(
rendering: ScreenT,
viewEnvironment: ViewEnvironment
)
}

/**
* A [ViewRegistry.Entry] that can build Android [View] instances, along with functions
* that can update them to display [Screen] renderings of a particular [type], bundled
Expand Down Expand Up @@ -165,6 +146,125 @@ public interface ScreenViewFactory<in ScreenT : Screen> : ViewRegistry.Entry<Scr
buildView(initialRendering, initialEnvironment, context, container)
}
}

/**
* Creates a [ScreenViewFactory] for [WrapperT] that finds and delegates to
* the one for [WrappedT]. Allows [WrapperT] to wrap instances of [WrappedT]
* to add information or behavior, without requiring wasteful wrapping in the view system.
*
* One general note: when creating a wrapper rendering, you're very likely to want it
* to implement [Compatible], to ensure that checks made to update or replace a view
* are based on the wrapped item. Each wrapper example below illustrates this.
*
* This a simpler variant of the like named function that takes three arguments, for
* use when there is no need to manipulate the [ScreenViewHolder].
*
* ## Examples
*
* To make one rendering type an "alias" for another -- that is, to use the
* same [ScreenViewFactory] to display it:
*
* class RealScreen(val data: String): AndroidScreen<RealScreen> {
* override val viewFactory = fromLayout<RealScreen>(...)
* }
*
* class AliasScreen(val similarData: String) : AndroidScreen<AliasScreen> {
* override val viewFactory = forWrapper<AliasScreen, RealScreen> { aliasScreen ->
* RealScreen(aliasScreen.similarData)
* }
* }
*
* To make one [Screen] type a wrapper for others:
*
* class Wrapper<W>(val wrapped: W: Screen) : AndroidScreen<Wrapper<W>>, Compatible {
* override val compatibilityKey = Compatible.keyFor(wrapped)
* override val viewFactory = ScreenViewFactory.forWrapper<Wrapper<W>, W> { it.wrapped }
* }
*
* To make a wrapper that adds information to the [ViewEnvironment]:
*
* class ReverseNeutronFlowPolarity : ViewEnvironmentKey<Boolean>(Boolean::class) {
* override val default = false
* }
*
* class ReversePolarityScreen<W : Screen>(
* val wrapped: W
* ) : AndroidScreen<ReversePolarityScreen<W>>, Compatible {
* override val compatibilityKey: String = Compatible.keyFor(wrapped)
* override val viewFactory = forWrapper<OverrideNeutronFlow<W>, Screen> {
* it.wrapped.withEnvironment(
* Environment.EMPTY + (ReverseNeutronFlowPolarity to true)
* )
* }
* }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome examples! Thank-you!

*
* @param unwrap a function to extract [WrappedT] instances from [WrapperT]s.
*/
@WorkflowUiExperimentalApi
public inline fun <
reified WrapperT : Screen,
WrappedT : Screen
> forWrapper(
crossinline unwrap: (wrapperScreen: WrapperT) -> WrappedT,
): ScreenViewFactory<WrapperT> = forWrapper(
unwrap = unwrap,
beforeShowing = {}
) { _, wrapper, e, showWrapper ->
showWrapper(unwrap(wrapper), e)
}

/**
* Creates a [ScreenViewFactory] for [WrapperT] that finds and delegates to
* the one for [WrappedT]. Allows [WrapperT] to wrap instances of [WrappedT]
* to add information or behavior, without requiring wasteful wrapping in the view system.
*
* This fully featured variant of the function is able to initialize the freshly
* created [ScreenViewHolder], and transform the wrapped [ScreenViewHolder.runner].
*
* To make a wrapper that customizes [View] initialization:
*
* class WithTutorialTips<W : Screen>(
* val wrapped: W
* ) : AndroidScreen<WithTutorialTips<W>>, Compatible {
* override val compatibilityKey = Compatible.keyFor(wrapped)
* override val viewFactory = forWrapper<WithTutorialTips<W>, W>(
* unwrap = { it.wrapped },
* beforeShowing = { TutorialTipRunner.initialize(it.view) },
* showWrapperScreen = { _, wrapper, environment, showWrapper ->
* showWrapper(unwrap(wrapper), environment)
* }
* )
* }
*
* @param unwrap a function to extract [WrappedT] instances from [WrapperT]s.
*
* @param beforeShowing a function to be invoked immediately after a new [View] is built.
*
* @param showWrapperScreen a function to be invoked when an instance of [WrapperT] needs
* to be shown in a [View] built to display instances of [WrappedT]. Allows
* pre- and post-processing of the [View].
*/
@WorkflowUiExperimentalApi
public inline fun <
reified WrapperT : Screen,
WrappedT : Screen
> forWrapper(
Comment on lines +248 to +251
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formatting looks odd to me? Can we move the generics up on the same line and the function name too?

crossinline unwrap: (wrapperScreen: WrapperT) -> WrappedT,
crossinline beforeShowing: (viewHolder: ScreenViewHolder<WrapperT>) -> Unit = {},
crossinline showWrapperScreen: (
view: View,
wrapperScreen: WrapperT,
environment: ViewEnvironment,
showUnwrappedScreen: (WrappedT, ViewEnvironment) -> Unit
) -> Unit,
): ScreenViewFactory<WrapperT> =
fromCode { initialRendering, initialEnvironment, context, container ->
val wrappedFactory = unwrap(initialRendering).toViewFactory(initialEnvironment)
val wrapperFactory = wrappedFactory.toUnwrappingViewFactory(unwrap, showWrapperScreen)
wrapperFactory.buildView(
initialRendering, initialEnvironment, context, container
).also { beforeShowing(it) }
}
}
}

Expand Down Expand Up @@ -266,140 +366,18 @@ public fun <ScreenT : Screen> ScreenViewFactory<ScreenT>.startShowing(
*/
@WorkflowUiExperimentalApi
public fun interface ViewStarter {
/** Called from [View.start]. [doStart] must be invoked. */
/** Called from [ScreenViewFactory.startShowing]. [doStart] must be invoked. */
public fun startView(
view: View,
doStart: () -> Unit
)
}

/**
* Transforms a [ScreenViewFactory] of [WrappedT] into one that can handle
* instances of [WrapperT]. Allows [WrapperT] to wrap instances of [WrappedT]
* to add information or behavior, without requiring wasteful wrapping in the view system.
*
* One general note: when creating a wrapper rendering, you're very likely to want it
* to implement [Compatible], to ensure that checks made to update or replace a view
* are based on the wrapped item. Each wrapper example below illustrates this.
*
* This a simpler variant of the like named function that takes two arguments, for
* use when there is no need to customize the [view][ScreenViewHolder.view] or
* the [environment][ScreenViewHolder.environment].
*
* ## Examples
*
* To make one rendering type an "alias" for another -- that is, to use the
* same [ScreenViewFactory] to display it:
*
* class RealScreen(val data: String): Screen
* object RealScreenViewFactory = ScreenViewFactory.fromLayout(...)
*
* class AliasScreen(val similarData: String) : Screen
*
* object AliasScreenViewFactory =
* RealScreenViewFactory.toUnwrappingViewFactory<AliasScreen, RealScreen> { aliasScreen ->
* RealScreen(aliasScreen.similarData)
* }
*
* To make one rendering type a wrapper for others:
*
* class Wrapper<W>(val wrapped: W: Screen) : Screen, Compatible {
* override val compatibilityKey = Compatible.keyFor(wrapped)
* }
*
* fun <W : Screen> WrapperViewFactory() =
* ScreenViewFactory.forBuiltView<Wrapper<W>> { wrapper, env, context, container ->
* // Get the view factory of the wrapped screen.
* wrapper.wrapped.toViewFactory(env)
* // Transform it to factory that accepts Wrapper<W>
* .toUnwrappingViewFactory<Wrapper<W>, W> { it.wrapped }
* // Delegate to the transformed factory to build the view.
* .buildView(wrapper, env, context, container)
* }
*
* To make a wrapper that adds information to the [ViewEnvironment]:
*
* class NeutronFlowPolarity(val reversed: Boolean) {
* companion object : ViewEnvironmentKey<NeutronFlowPolarity>(
* NeutronFlowPolarity::class
* ) {
* override val default: NeutronFlowPolarity =
* NeutronFlowPolarity(reversed = false)
* }
* }
*
* class OverrideNeutronFlow<W : Screen>(
* val wrapped: W,
* val polarity: NeutronFlowPolarity
* ) : Screen, Compatible {
* override val compatibilityKey: String = Compatible.keyFor(wrapped)
* }
*
* fun <W : Screen> OverrideNeutronFlowViewFactory() =
* ScreenViewFactory.forBuiltView<OverrideNeutronFlow<W>> { wrapper, env, context, container ->
* // Get the view factory of the wrapped screen.
* wrapper.wrapped.toViewFactory(env)
* // Transform it to factory that accepts OverrideNeutronFlow<W>, by
* // replacing the OverrideNeutronFlow<W> with an EnvironmentScreen<W>
* .toUnwrappingViewFactory<OverrideNeutronFlow<W>, EnvironmentScreen<W>> {
* it.wrapped.withEnvironment(
* Environment.EMPTY + (NeutronFlowPolarity to it.polarity)
* )
* }
* // Delegate to the transformed factory to build the view.
* .buildView(wrapper, env, context, container)
* }
*
* @param unwrap a function to extract [WrappedT] instances from [WrapperT]s.
*/
@WorkflowUiExperimentalApi
public inline fun <
reified WrapperT : Screen,
WrappedT : Screen
> ScreenViewFactory<WrappedT>.toUnwrappingViewFactory(
crossinline unwrap: (wrapperScreen: WrapperT) -> WrappedT,
): ScreenViewFactory<WrapperT> =
toUnwrappingViewFactory(unwrap) { _, ws, e, su -> su(unwrap(ws), e) }
Comment on lines -356 to -362
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already regret deleting this, probably going to bring it back.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, still no use case.


/**
* Transforms a [ScreenViewFactory] of [WrappedT] into one that can handle
* instances of [WrapperT].
*
* One general note: when creating a wrapper rendering, you're very likely to want it
* to implement [Compatible], to ensure that checks made to update or replace a view
* are based on the wrapped item. Each wrapper example below illustrates this.
*
* Also see the simpler variant of this function that takes only an [unwrap] argument.
*
* ## Example
*
* To make a wrapper that customizes [View] initialization:
*
* class WithTutorialTips<W : Screen>(val wrapped: W) : Screen, Compatible {
* override val compatibilityKey = Compatible.keyFor(wrapped)
* }
*
* fun <W: Screen> WithTutorialTipsFactory<W>() =
* ScreenViewFactory.forBuiltView<WithTutorialTips<*>> = {
* initialRendering, initialEnv, context, container ->
* // Get the view factory of the wrapped screen.
* initialRendering.wrapped.toViewFactory(initialEnv)
* // Transform it to factory that accepts WithTutorialTips<W>
* .toUnwrappingViewFactory<WithTutorialTips<W>, W>(
* unwrap = { it.wrapped },
* showWrapperScreen = { view, withTips, env, showUnwrapped ->
* TutorialTipRunner.run(view)
* showUnwrapped(withTips.wrapped, env)
* }
* // Delegate to the transformed factory to build the view.
* .buildView(initialRendering, initialEnv, context, container)
* }
*
* @param unwrap a function to extract [WrappedT] instances from [WrapperT]s.
*
* @param showWrapperScreen a function invoked when an instance of [WrapperT] needs
* to be shown in a [View] built to display instances of [WrappedT]. Allows
* pre- and post-processing of the [View].
* It is usually more convenient to call [ScreenViewFactory.forWrapper].
*/
@WorkflowUiExperimentalApi
public inline fun <
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.squareup.workflow1.ui

import com.squareup.workflow1.ui.ScreenViewFactory.Companion.forWrapper
import com.squareup.workflow1.ui.container.BackStackScreen
import com.squareup.workflow1.ui.container.BackStackScreenViewFactory
import com.squareup.workflow1.ui.container.BodyAndModalsContainer
Expand Down Expand Up @@ -70,7 +71,7 @@ public interface ScreenViewFactoryFinder {
BodyAndModalsContainer as ScreenViewFactory<ScreenT>
}
?: (rendering as? NamedScreen<*>)?.let {
NamedScreenViewFactory<ScreenT>() as ScreenViewFactory<ScreenT>
forWrapper<NamedScreen<ScreenT>, ScreenT> { it.wrapped } as ScreenViewFactory<ScreenT>
}
?: (rendering as? EnvironmentScreen<*>)?.let {
EnvironmentScreenViewFactory<ScreenT>() as ScreenViewFactory<ScreenT>
Expand Down
Loading