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

Provide a way to calculate ComposeScene preferredSize that is needed for Swing #884

Merged
merged 11 commits into from
Nov 16, 2023

Conversation

Walingar
Copy link
Collaborator

Proposed Changes

Previous solution with preferred size didn't work well because constraints with maxSize taken from component.size were used, but component.size is not set (0 by default).

See issue: JetBrains/compose-multiplatform#3834

By Swing design preferred size should be calculated without incoming constraints. It is not possible in Compose, so I artificially set max constraints taking sum of displays dimensions here. Not the best solution, but I couldn't imagine better one..

Testing

Test: run ComposePanelTest.kt

Issues Fixed

Fixes: JetBrains/compose-multiplatform#3834

internal fun measureInConstraints(constraints: Constraints): IntSize {
val oldConstraints = this.constraints
try {
// TODO: is it possible to measure without reassigning root constraints?
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 couldn't find a better way to measure in given constraints...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also, as I understand it will break layout caches...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But it should be called on parent relayout which should happen a lot

Copy link
Collaborator

Choose a reason for hiding this comment

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

I checked, and Swing doesn't call getPreferredSize on window resize in the initial panel size of LazyColumn with border layout test. So, we are safe, and we only have an additional measure at startup?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It depends on LayoutManager, in case of window resize everything should be fine, since PreferredSize is used only for initial size. But other LayoutManagers may call preferredSize any time :(

val mainOwner = mainOwner ?: return IntSize.Zero
mainOwner.measureAndLayout()
return mainOwner.contentSize
return contentSizeInConstraints(mainOwner.constraints)
Copy link

Choose a reason for hiding this comment

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

We should cache this or turn it into a function. Code that previously assumed this was a quick operation will need to be rewritten. For example in SemanticsNode.isInScreenBounds():

    return nodeBoundsInWindow.top >= 0 &&
        nodeBoundsInWindow.left >= 0 &&
        nodeBoundsInWindow.right <= composeView.contentSize.width &&
        nodeBoundsInWindow.bottom <= composeView.contentSize.height

Copy link

Choose a reason for hiding this comment

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

Actually, are you sure this isn't breaking anything? Before, contentSize was the actual size of the scene. Now it's the result of some computation - why is it guaranteed to match the real size of the scene?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, I missed this particular case, indeed this one can break, thank you

Copy link
Collaborator Author

@Walingar Walingar Nov 13, 2023

Choose a reason for hiding this comment

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

Mentioned code was recently changed by Ivan. Also, this API always was not that cheap, since it did measure and layout.
So, nothing is changed here, code still works the same

* Provides a way to measure Owner's content in given [constraints]
* Draw/pointer and other callbacks won't be called here like in [measureAndLayout] functions
*/
internal fun measureInConstraints(constraints: Constraints): IntSize {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's make it public and experimental. The policy for ComposeScene - if something is needed for one platform, it can be needed for other platforms, implemented by users.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, let's add TODO for contentSize:

TODO: investigate all internal usages, and deprecated it, if it isn't actually needed

I found one usage in ui-test, and I am not sure that is correct usage of contentSize there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It seems that this is not used anymore. Anyway, contentSize api is not changed. It still returns the size in mainOwner constraints

Copy link

Choose a reason for hiding this comment

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

@igordmn contentSize is public API, and calculateContentSize is not a replacement, as it does something else entirely.

internal fun measureInConstraints(constraints: Constraints): IntSize {
val oldConstraints = this.constraints
try {
// TODO: is it possible to measure without reassigning root constraints?
Copy link
Collaborator

Choose a reason for hiding this comment

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

I checked, and Swing doesn't call getPreferredSize on window resize in the initial panel size of LazyColumn with border layout test. So, we are safe, and we only have an additional measure at startup?

val mainOwner = mainOwner ?: return IntSize.Zero
mainOwner.measureAndLayout()
return mainOwner.contentSize
return contentSizeInConstraints(mainOwner.constraints)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just note, that after my changes this code doesn't perform layout anymore

@Walingar
Copy link
Collaborator Author

@igordmn @m-sasha Folks, sorry for the delay. I've answered on comments and pushed recent changes, please take a look.

I've changed max constraints to infinity, so exception will be thrown if user doesn't provide maxSize for lazy layouts.
Also, I provided experimental API in ComposeScene since it is needed by convention of ComposeScene.

It seems that ComposeScene.contentSize is not used anymore in our codebase. But since it is public API, I keep it here. The only change is that it doesn't perform layout

Walingar and others added 2 commits November 14, 2023 13:42
Copy link
Collaborator

@igordmn igordmn left a comment

Choose a reason for hiding this comment

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

LGTM. Let's wait for the second approval from @m-sasha

@m-sasha
Copy link

m-sasha commented Nov 16, 2023

Don't know how important this is, but some of the javadocs exceed 100 columns.

*/
// TODO: make stable and deprecate contentSize in 1.7
@ExperimentalComposeUiApi
fun calculateContentSize(): IntSize {
Copy link

Choose a reason for hiding this comment

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

Maybe call it preferredSize()?

@@ -477,14 +477,36 @@ class ComposeScene internal constructor(
/**
* Returns the current content size
*/
// TODO: make calculateContentSize stable in 1.7 and mark it as
// @Deprecated("Use calculateContentSize() instead", replaceWith = ReplaceWith("calculateContentSize()"))
Copy link

Choose a reason for hiding this comment

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

It seems to me that "current content size" and "preferred size" (in infinite constraints) are not at all the same thing. If so, why do we want to deprecate getting the current size?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@igordmn told that by design contentSize was expected to provide size in infinity constraints, so there is no usecase to get current size and should be received in another way

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, there wasn't an intention to provide current content size, and I am not aware of use cases where we need it. It was added to determine the size of the window/panel before its construction.

* Provides a way to measure Owner's content in given [constraints]
* Draw/pointer and other callbacks won't be called here like in [measureAndLayout] functions
*/
internal fun measureInConstraints(constraints: Constraints): IntSize {
Copy link

Choose a reason for hiding this comment

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

@igordmn contentSize is public API, and calculateContentSize is not a replacement, as it does something else entirely.

@m-sasha m-sasha self-requested a review November 16, 2023 15:18
@Walingar Walingar merged commit f01d58e into jb-main Nov 16, 2023
3 of 4 checks passed
@Walingar Walingar deleted the nr/preferred-size branch November 16, 2023 15:24
mazunin-v-jb pushed a commit that referenced this pull request Dec 7, 2023
…for Swing (#884)

---------

Co-authored-by: Igor Demin <igordmn@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ComposePanel doesn't calculate its preferredSize correctly when it's called before doLayout
4 participants