Skip to content

Conversation

@rjrjr
Copy link
Collaborator

@rjrjr rjrjr commented Apr 16, 2021

That was always a weird coupling, and it caused the nasty compounding double
update behavior in DecorativeViewFactory.

To prevent ViewFactory implementations from having to remember to call
showRendering, we make ViewRegistry.buildView do so. And to allow more
sophisticiated ViewFactory implementations to prevent that call from
happening prematurely, we build it into the default value for a new
initializeView method on ViewRegistry.buildView.

DecorativeViewFactory takes advantage by passing a no-op function in for
initializeView when the view is built, and then making its own explicit call
to showRendering after it's done playing wrapping games.

Closes #397

@rjrjr rjrjr force-pushed the ray/decor-397-396 branch 6 times, most recently from e47cb6f to a301ef5 Compare April 16, 2021 01:03
@rjrjr rjrjr marked this pull request as ready for review April 16, 2021 01:06
@rjrjr rjrjr requested review from a team and zach-klippenstein as code owners April 16, 2021 01:06
@rjrjr rjrjr requested a review from laurakelly April 16, 2021 01:06
@zach-klippenstein
Copy link
Collaborator

zach-klippenstein commented Apr 16, 2021

Haven't started reading the actual PR yet, but just some comments from the description:

Made it that much easier for ViewRegistry.buildView
to invoke it

Minor implementation inconvenience isn't a great reason to make API changes.

I suspect it will come in handy in other places too.

Maybe, but I'd rather keep it locked down until we prove that, so we don't have to deprecated it later. I don't know if we will, either, because showRendering typically needs to have either a more concretely-typed view, or is using cached findViewById results from one.

@rjrjr rjrjr force-pushed the ray/decor-397-396 branch from a301ef5 to e0a3ee4 Compare April 16, 2021 01:30
): DialogRef<Any> {
val view = initialViewEnvironment[ViewRegistry]
.buildView(initialModalRendering, initialViewEnvironment, this)
.buildView(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This change and a couple of others like it are because I deleted a convenience overload of buildView that caused nothing but trouble, and which in practice no one but the library will ever use.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes! Figured that's what was going on, and I've wanted to do that in the past too. Thanks!

@rjrjr rjrjr force-pushed the ray/decor-397-396 branch 3 times, most recently from b3b1725 to a6807d9 Compare April 16, 2021 01:46
@rjrjr
Copy link
Collaborator Author

rjrjr commented Apr 16, 2021

Haven't started reading the actual PR yet, but just some comments from the description:

Made it that much easier for ViewRegistry.buildView
to invoke it

Minor implementation inconvenience isn't a great reason to make API changes.

I suspect it will come in handy in other places too.

Maybe, but I'd rather keep it locked down until we prove that, so we don't have to deprecated it later. I don't know if we will, either, because showRendering typically needs to have either a more concretely-typed view, or is using cached findViewById results from one.

Yeah, it was a bad call. Turns out I didn't even use it. PR is smaller now.

): DialogRef<Any> {
val view = initialViewEnvironment[ViewRegistry]
.buildView(initialModalRendering, initialViewEnvironment, this)
.buildView(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes! Figured that's what was going on, and I've wanted to do that in the past too. Thanks!

That was always a weird coupling, and it caused the nasty compounding double
update behavior in `DecorativeViewFactory`.

To prevent `ViewFactory` implementations from having to remember to call
`showRendering`, we make `ViewRegistry.buildView` do so. And to allow more
sophisticiated `ViewFactory` implementations to prevent that call from
happening prematurely, we build it into the default value for a new
`initializeView` method on `ViewRegistry.buildView`.

`DecorativeViewFactory` takes advantage by passing a no-op function in for
`initializeView` when the view is built, and then making its own explicit call
to `showRendering` after it's done playing wrapping games.

Closes #397
@rjrjr rjrjr force-pushed the ray/decor-397-396 branch from a6807d9 to 487faf7 Compare April 16, 2021 15:29
@rjrjr rjrjr merged commit e4af35e into main Apr 16, 2021
@rjrjr rjrjr deleted the ray/decor-397-396 branch April 16, 2021 16:05
rjrjr added a commit that referenced this pull request Dec 9, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408). Unfortunately, that fix botched recursion.
Individual `DecorativeViewFactory` instances work fine, but if you wrap them
you still get one `showRendering` call from each. Worse, upstream
`initializeView` lambdas are clobbered by immediately downstream ones.
e.g., when a `WorkflowViewStub` shows a `DecorativeViewFactory`, the
`WorkflowLifecycleRunner.installOn` call in the former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all. The `initializeView` lambdas are gone,
and so is `View.showFirstRendering`.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for calling a new method, `View.start`, before it makes any calls to
`View.showRendering`. `View.start` takes over the
`WorkflowLifecycleRunner.installOn` job that was previously done by
`WorkflowViewStub` and a few others. Since it is called only after any
`ViewFactory` wrapping of the rendering in hand has been peformed, we're
certain it will only happen once.

Of course we still need the ability to to customize view initialization via
wrapping. To accomodate that, `View.start` is implemented by executing
another new extension, `internal var View.starter: (View) -> Unit`.
Because it is a `var`, `buildView` callers can wrap it before `start`
is invoked.

We provide a bit of hand holding to those who need this ability to wrap the
starter via `DecorativeViewFactory.onStartWrapper`, which replaceds
 `initializeView`. Where `initializeView` lambdas were expected to make their
own call to the static `View.showFirstRendering` function, `onStartWrapper`
is passed a function to invoke -- the current contents of `View.starter`.
We wrap and replace `var View.starter` for them, and keep it private
to the library.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 9, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408). Unfortunately, that fix botched recursion.
Individual `DecorativeViewFactory` instances work fine, but if you wrap them
you still get one `showRendering` call from each. Worse, upstream
`initializeView` lambdas are clobbered by immediately downstream ones.
e.g., when a `WorkflowViewStub` shows a `DecorativeViewFactory`, the
`WorkflowLifecycleRunner.installOn` call in the former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all. The `initializeView` lambdas are gone,
and so is `View.showFirstRendering`.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for calling a new method, `View.start`, before it makes any calls to
`View.showRendering`. `View.start` takes over the
`WorkflowLifecycleRunner.installOn` job that was previously done by
`WorkflowViewStub` and a few others. Since it is called only after any
`ViewFactory` wrapping of the rendering in hand has been peformed, we're
certain it will only happen once.

Of course we still need the ability to to customize view initialization via
wrapping. To accomodate that, `View.start` is implemented by executing
another new extension, `internal var View.starter: (View) -> Unit`.
Because it is a `var`, `buildView` callers can wrap it before `start`
is invoked.

We provide a bit of hand holding to those who need this ability to wrap the
starter via `DecorativeViewFactory.onStartWrapper`, which replaceds
 `initializeView`. Where `initializeView` lambdas were expected to make their
own call to the static `View.showFirstRendering` function, `onStartWrapper`
is passed a function to invoke -- the current contents of `View.starter`.
We wrap and replace `var View.starter` for them, and keep it private
to the library.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`, which is
renamed `WorkflowUiViewState.kt`. `WorkflowUiViewState` is a sealed class
that hangs off of a view tag, with two implementations (`New` and `Started`)
to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`
The `ViewShowRenderingTag` that it hangs off of a view tag is renamed
`WorkflowViewState`, and extracted to a separate file.

`WorkflowViewState` is a sealed class with two implementations (`New` and
`Started`) to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
rjrjr added a commit that referenced this pull request Dec 10, 2021
Fixes #597.

It used to be the case that every `DecorativeViewFactory` called
`showRendering()` twice (#397). We fixed that (we thought) by introducing the
`initializeView` lambda to `ViewRegistry.buildView` and
`DecorativeViewFactory` (#408).

Unfortunately, that fix botched recursion. Individual `DecorativeViewFactory`
instances work fine, but if you wrap them you still get one `showRendering`
call from each. Worse, upstream `initializeView` lambdas are clobbered by
immediately downstream ones. e.g., when a `WorkflowViewStub` shows a
`DecorativeViewFactory`, the `WorkflowLifecycleRunner.installOn` call in the
former is clobbered.

The fix is to completely decouple building a view from from this kind of
initialization. `ViewRegistry.buildView` and its wrappers no longer try to
call `showRendering` at all.

Instead the caller of `buildView` (mostly `WorkflowViewStub`) is reponsible
for immediately calling `View.start` on the new `View`. `View.start` makes
the initial `showRendering` call that formerly was the job of
`ViewFactory.buildView` -- the factory builds the view, and the container
turns the key. Since `View.start` is called only after all wrapped
`ViewFactory.buildView` functions have executed, we're certain it will only
happen once.

Of course we still need the ability to customize view initialization via
wrapping, especially to invoke `WorkflowLifecycleOwner.installOn`. To
accomodate that, the function that `View.start` executes can be wrapped via
the new `viewStarter` argument to `ViewRegistry.buildView` and
`DecorativeViewFactory`, which replaces `initializeView`.

This required a pretty thorough overhaul of `ViewShowRendering.kt`
The `ViewShowRenderingTag` that it hangs off of a view tag is renamed
`WorkflowViewState`, and extracted to a separate file.

`WorkflowViewState` is a sealed class with two implementations (`New` and
`Started`) to help us enforce the order of the `ViewRegistry.buildView`,
`View.bindShowRendering`, `View.start` and `View.showRendering` calls.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redundant calls to View.showRendering

3 participants