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

Introduces DialogCollator #981

Merged
merged 1 commit into from
May 15, 2023
Merged

Introduces DialogCollator #981

merged 1 commit into from
May 15, 2023

Conversation

rjrjr
Copy link
Contributor

@rjrjr rjrjr commented Mar 29, 2023

Android notoriously doesn't give us any control over the z order of Dialog windows
other than taking care to show() them in the right order, so keeping a pile
of windows in sync with a list of Overlay rendering models is hard.

We've already solved that for individual instances of LayeredDialogSessions
(the delegate that does all of the work for BodyAndOverlaysContainer), but
it was still a problem when you nested them, e.g. in the style of the
recently introduced Nested Overlays sample. Also, the solution was kind of
nasty because we would replace any out of order Dialogs with brand new ones,
rather than preserving them by calling dismiss() and show().

To fix all that we introduce internal class DialogCollator. On each view
update pass, a new DialogCollator instance is created by the outermost
LayeredDialogSession and made available to any nested instances via the
ViewEnvironment. The shared collator collects a list of all existing
DialogSession instances, and a set of DialogSessionUpdate objects to be
asynchronously used to update existing instances or create new ones.

Because a DialogCollator has complete knowledge of all the windows managed
by the set of nested LayerDialogSession instances it supports, it is able to
ensure that inserts are supported by calling dismiss() / show() on existing
windows in the sequence needed to reorder them.

One important change to note: our SavedStateRegistry code requires that each
window in a set has a unique name, which we derive by applying Compatible.keyFor to
its defining Overlay. We used to append to that key an Overlay's position in its
LayeredDialogSessions list, as a poorly considered hack to simplify showing
several of the same type at once. That approach conflicts with reordering.

With this commit we no longer include the index value in SavedStateRegistry keys,
meaning that each Overlay shown by a LayeredDialogSession must have a unique
key -- BackStackScreen has long had a similar requirement. NamedScreen
exists to simplify that kind of thing, and in particular can be used to wrap
ScreenOverlay.content to meet the new requirement.

Fixes #966

@rjrjr rjrjr force-pushed the ray/coordinated-dialogs branch 3 times, most recently from 6fc1a62 to 87819fc Compare March 29, 2023 20:46
@rjrjr rjrjr marked this pull request as ready for review March 29, 2023 20:47
@rjrjr rjrjr requested review from a team and zach-klippenstein as code owners March 29, 2023 20:47
@rjrjr rjrjr force-pushed the ray/coordinated-dialogs branch from 87819fc to 1b90f84 Compare March 29, 2023 21:29
@rjrjr rjrjr force-pushed the ray/coordinated-dialogs branch from 1b90f84 to bc40918 Compare March 29, 2023 23:01
@vgonda
Copy link

vgonda commented Mar 30, 2023

Lol @square/ui-systems-android was literally just talking about how painful dialogs can be and if it would be less painful to draw them manually 🙊

@rjrjr
Copy link
Contributor Author

rjrjr commented Mar 30, 2023 via email

@rjrjr rjrjr marked this pull request as draft March 30, 2023 17:17
@rjrjr
Copy link
Contributor Author

rjrjr commented Mar 30, 2023

Moved this back to a draft, Square's internal test suite has issues with it.

Screenshot 2023-03-30 at 10 16 54 AM

@rjrjr rjrjr force-pushed the ray/coordinated-dialogs branch 3 times, most recently from f024178 to 6b4cc6c Compare March 30, 2023 21:54
@rjrjr
Copy link
Contributor Author

rjrjr commented May 11, 2023

One vacation and one sick bed later, this has passed our internal test suite with flying colors.

@rjrjr rjrjr marked this pull request as ready for review May 11, 2023 02:48
Copy link
Contributor

@steve-the-edwards steve-the-edwards left a comment

Choose a reason for hiding this comment

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

Lots of kdoc/understandability questions.

I'm going to have to trust the tests for this to a certain extent as, as you noted, its very complex!

Would be great to have a class diagram and a mock up of how that maps to your main example app used for tests.

But ... maybe there is not need for that as this code will hopefully not be often maintained?

@rjrjr
Copy link
Contributor Author

rjrjr commented May 11, 2023

Great feedback, thanks @steve-the-edwards.

@rjrjr rjrjr force-pushed the ray/coordinated-dialogs branch 2 times, most recently from 49b2883 to 41be190 Compare May 13, 2023 18:52
@rjrjr rjrjr requested a review from steve-the-edwards May 14, 2023 00:47
Copy link
Contributor

@steve-the-edwards steve-the-edwards left a comment

Choose a reason for hiding this comment

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

Nice. I understand better now I think! Can you contribute this back to Android 🙏🏻 😆


oldSessionOrNull
?.also { session -> session.show(overlay, dialogEnv) }
?: overlay.toDialogFactory(dialogEnv)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: The work here to set up a new session is still a bit hidden after the elvis operator.

I think its fine but a comment might help, "If we don't have an old session to update, create a new session and initialize it to show the dialog in this environment."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

        oldSessionOrNull
          // If there is already a matching session, it may have been hidden b/c out of order.
          ?.also { session -> session.show(overlay, dialogEnv) }
          // This is a new Overlay, create and show its Dialog.

Android notoriously give us no control over the z order of Dialog windows
other than taking care to `show()` them in the right order, so keeping a pile
of windows in sync with a list of `Overlay` rendering models is hard.

We've already solved that for individual instances of `LayeredDialogSessions`
(the delegate that does all of the work for `BodyAndOverlaysContainer`), but
it was still a problem when you nested them, e.g. in the style of the
recently introduced Nested Overlays sample. Also, the solution was kind of
nasty because we would replace any out of order Dialogs with brand new ones,
rather than preserving them by calling `dismiss()` and `show()`.

To fix all that we introduce `internal class DialogCollator`. On each view
update pass, a new `DialogCollator` instance is created by the outermost
`LayeredDialogSessions` and made available to any nested instances via the
`ViewEnvironment`. The shared collator collects a list of all existing
`DialogSession` instances, and a set of `DialogSessionUpdate` functions to be
asynchronously used to update existing instances or create new ones.

Because a `DialogCollator` has complete knowledge of all the windows managed
by the set of nested `LayerDialogSessions` instances it supports, it is able to
ensure that inserts are supported by calling `dismiss()` / `show()` on existing
windows in the sequence needed to reorder them.

One important change to note: our `SavedStateRegistry` code requires that each
window in a set has a unique name, which we derive by applying `Compatible.keyFor` to
its defining `Overlay`. We used to append to that key an `Overlay`'s position in its
`LayeredDialogSessions` list, as a poorly considered hack to simplify showing
several of the same type at once. That approach conflicts with reordering.

With this commit we no longer include the index value in `SavedStateRegistry` keys,
meaning that each `Overlay` shown by a `LayeredDialogSession` must have a unique
key -- `BackStackScreen` has long had a similar requirement. `NamedScreen`
exists to simplify that kind of thing, and in particular can be used to wrap
`ScreenOverlay.content` to meet the new requirement.

Fixes #966
@rjrjr rjrjr force-pushed the ray/coordinated-dialogs branch from 41be190 to e081c2e Compare May 15, 2023 17:34
@rjrjr rjrjr enabled auto-merge May 15, 2023 17:37
@rjrjr rjrjr merged commit c056e1c into main May 15, 2023
@rjrjr rjrjr deleted the ray/coordinated-dialogs branch May 15, 2023 18:04
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.

Dialog order is not preserved
5 participants