-
Notifications
You must be signed in to change notification settings - Fork 102
Add SwingFrame and SwingDialog composables that accept an init parameter
#2139
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
Conversation
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Window.desktop.kt
Outdated
Show resolved
Hide resolved
| */ | ||
| @ExperimentalComposeUiApi | ||
| @Composable | ||
| fun SwingFrame( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| fun SwingFrame( | |
| fun Window( |
We need to be consistent with the current names, even if they aren't good.
The current name for creating ComposeWindow is also Window:

I.e., at the moment there are 2 Window - one tries to hide ComposeWindow as implementation details, one is an additional overload, specifically for ComposeWindow. In this PR we add one more overload for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it, but
- From experience, it becomes very confusing to select the right overload. It's already pretty confusing with the overloads we have.
- I would also like to strongly indicate to the user that this is an API that is lower-level than
Window. - Having
Windowfor creatingComposeWindow, as you mentioned yourself, is possibly a mistake. We would preferWindowto be an API that's not tied to AWT (except it's too late now).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an existing example, we have AwtWindow...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having Window for creating ComposeWindow, as you mentioned yourself, is possibly a mistake.
It was about FrameWindowScope.window available in the AWT-independent arguments overload.
The another overload that has create, update, dispose tied to ComposeWindow is intentional and isn't a mistake. For example, we could add a JavaFX overload:

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, consistency is more important than the right names.
If we want the right names, we also need to rename Window(create= to ComposableJFrame, and ComposeWindow to ComposeJFrame, with deprecation of the old names. We can do that, but we should do that in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an existing example, we have AwtWindow...
It is a mess, but the new function is related to the function I posted in the screenshot, not to AwtWindow
| */ | ||
| @ExperimentalComposeUiApi | ||
| @Composable | ||
| fun SwingFrame( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having 2 functions with just one additional argument isn't good for maintenance.
The semantic of these functions is the same. It is only the parameter init is different.
Better to add init to the original functions, and add an old method with Deprecated(HIDDEN) for backward compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From our discussion, I thought we agreed that we want to give indication to the user that this is a lower-level, AWT-specific API. I specifically don't want to expose the user to init, unless he has a specific need to go low-level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, may be I am wrong. If, for example, we wanted to add JavaFX window overload, we couldn't do that 🤔. Will think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that we want to give indication to the user that this is a lower-level
In my thoughts, this is not a question about if the function is low-level, it is only about the specific argument init that is low-level. Either way if we have 1 or 2 functions, we have to describe all that only in the init parameter description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, may be I am wrong. If, for example, we wanted to add JavaFX window overload, we couldn't do that 🤔. Will think
Ok, I am definitely wrong wanting init in the main function, as:
- it is tied to a specific implementation
- we might want to add another implementation.
So, let's keep separately, as you have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But let's add a link to a new function in the main function in // comment, so developers don't forget to add new parameters there.
|
As it is a new API, please add a second reviewer. |
| onPreviewKeyEvent: (KeyEvent) -> Boolean = { false }, | ||
| onKeyEvent: (KeyEvent) -> Boolean = { false }, | ||
| create: () -> ComposeWindow, | ||
| init: (ComposeWindow) -> Unit, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the semantic difference between init and create here? Cannot be init just a part of create?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a difference both in the intent and in the behavior.
The intent is that configuration (init) is separate from creation of the window.
The behavior is that init is called after all the "create" parts completed. It can't be folded into create itself because create is called first, then this function does stuff (setting rootForTestListener etc.) and then init is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully follow why it cannot be folded and why setting listeners should be done before "init"
Can we keep this reverted dependency to avoid replicating AWT/Swing API? I mean providing overload to receive AWT/Swing object outside (or lambda factory) and deal with already constructed/initialized object here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully follow why it cannot be folder and why setting listeners should be done before "init"
I don't think it actually does matter now, but it could matter in the future. That code may want to set some property of the window that the user may want to override, so its best to first call all of our code, and then the user code.
It's not a big deal, but I think it would actually be best to refactor this:
- Add an
initargument toAwtWindow. - Move all the code configuring the window into
init, leaving only the creation itself increate.
@igordmn What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it actually does matter now, but it could matter in the future.
For me init was always part of creation process, unless something else is specified initContext(adapter), etc.
Having both init and create: () -> is confusing for me.
That code may want to set some property of the window that the user may want to override, so its best to first call all of our code
What is set inside AwtWindow/SwingWindow (rootForTestListener, compositionLocalContext, exceptionHandler, and isVisible) is either needed for proper work of these Composable, or is read from the outside world (from locals). So, either it is wrong wanting to override the properties (it breaks the Composable), or there is another way (override the locals). All other types of properties shouldn't be set inside these functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is set inside AwtWindow/SwingWindow (rootForTestListener, compositionLocalContext, exceptionHandler, and isVisible) is either needed for proper work of these Composable, or is read from the outside world (from locals).
That's true now, but won't necessarily be true in the future.
Anyway, I don't have a strong conviction about this, so I'll fold init into create if you both think it's the right choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but won't necessarily be true in the future.
I wrote that it shouldn't be true:
All other types of properties shouldn't be set inside these functions.
If we add such properties, it will be a mistake.
5cf1e66 to
c59e272
Compare
|
Rebased on Please re-review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No objections from my side, but the main review / approval is up to @igordmn
It is currently impossible to configure Window/Dialog properties which may only be set before the window/dialog is displayable. This PR adds
SwingWindowandSwingDialogfunctions which take the same arguments asWindowandDialogWindow, but also add aninitblock which is called before the window/dialog is made displayable.Fixes https://youtrack.jetbrains.com/issue/CMP-6949/window.type-Window.Type.UTILITY-is-not-available
Testing
Added tests and also tested
Window.setTypemanually.This could be tested by QA
Release Notes
Features - Desktop
SwingFrameandSwingDialogcomposables that allow configuring the window/dialog before it is shown.