Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redundant calls to View.showRendering #397

Closed
rjrjr opened this issue Mar 31, 2021 · 3 comments · Fixed by #408
Closed

Redundant calls to View.showRendering #397

rjrjr opened this issue Mar 31, 2021 · 3 comments · Fixed by #408
Assignees
Labels
ui Related to UI integration

Comments

@rjrjr
Copy link
Contributor

rjrjr commented Mar 31, 2021

DecorativeViewFactory makes redundant calls to showRendering, and these can compound. I think the proper fix is to factor a rebindShowRendering call out of bindShowRendering that replaces the function but doesn't call it.

@rjrjr rjrjr added the ui Related to UI integration label Mar 31, 2021
@zach-klippenstein
Copy link
Collaborator

I agree this is a problem, I'm not sure I agree with your proposed solution. The root of the issue is the logic in DecoratorViewFactory that actually calls bind twice. Your proposed solution is one way to fix that, but I'm not sure it's the best one. For one, the contrast between bind/rebind doesn't imply to me that one calls showRendering and the other doesn't. For another, it might be better to add better support for the thing that DecoratorViewFactory is ultimately trying to do – I played around with this a few months back, via an API for effectively intercepting the view after it's created and the rendering tag is set but before showRendering is called, which allows the interceptor to decide whether or not to actually call showRendering.

Your solution might actually be simpler, but we should consider the tradeoffs.

@rjrjr
Copy link
Contributor Author

rjrjr commented Mar 31, 2021

I think this is a "why not both" situation. DecorativeViewFactory def. needs some love (e.g. #396), and separating the two inappropriately coupled concerns in bindShowRendering will make it easier to improve. We can come up with better names than bind and rebind -- perhaps View.initialize is the new name for bindShowRendering.

Note to self, here's the previous attempt: #276

@rjrjr rjrjr added this to To do in Workflow Kotlin via automation Apr 7, 2021
@rjrjr rjrjr moved this from To do to In progress in Workflow Kotlin Apr 7, 2021
@rjrjr rjrjr self-assigned this Apr 7, 2021
@rjrjr
Copy link
Contributor Author

rjrjr commented Apr 15, 2021

Really, bindShowRendering was just a mistake. I was worried about people forgetting to make the initial call to showRendering, so I tried to make that impossible and coupled the concerns. This was before WorkflowViewStub, when I expected it to be a lot more common to call these methods directly.

rjrjr added a commit that referenced this issue 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 moved this from In progress to Review in progress in Workflow Kotlin Apr 16, 2021
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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.

Another interesting change is that the `showRendering` function now gets the
`View` as its receiver. Made it that much easier for `ViewRegistry.buildView`
to invoke it, and I suspect it will come in handy in other places too.

Closes #397
rjrjr added a commit that referenced this issue 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 added a commit that referenced this issue 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 added a commit that referenced this issue 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 added a commit that referenced this issue 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 moved this from Review in progress to Reviewer approved in Workflow Kotlin Apr 16, 2021
Workflow Kotlin automation moved this from Reviewer approved to Done Apr 16, 2021
rjrjr added a commit that referenced this issue 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 issue 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 issue 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 issue 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 issue 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 issue 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 issue 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 issue 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 issue 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 issue 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
ui Related to UI integration
Projects
Development

Successfully merging a pull request may close this issue.

2 participants