-
Notifications
You must be signed in to change notification settings - Fork 117
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
Fix bounds of ComposePanel in IntelliJ on macOs #988
Changes from 12 commits
cf1cc55
547ac25
a0d2b16
56200f3
6d4dcd5
b0aa9f5
b6fffaf
3d9124f
62394a4
ee5bcba
e97db0b
7e0277b
5386b5a
b036927
f54ed82
e1c1c6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ import javax.swing.JPanel | |
import javax.swing.SwingUtilities | ||
import javax.swing.SwingUtilities.isEventDispatchThread | ||
import javax.swing.UIManager | ||
import javax.swing.event.AncestorEvent | ||
import javax.swing.event.AncestorListener | ||
|
||
actual open class SkiaLayer internal constructor( | ||
externalAccessibleFactory: ((Component) -> Accessible)? = null, | ||
|
@@ -133,6 +135,16 @@ actual open class SkiaLayer internal constructor( | |
@Suppress("LeakingThis") | ||
add(backedLayer) | ||
|
||
addAncestorListener(object : AncestorListener { | ||
override fun ancestorAdded(event: AncestorEvent?) = Unit | ||
|
||
override fun ancestorRemoved(event: AncestorEvent?) = Unit | ||
|
||
override fun ancestorMoved(event: AncestorEvent?) { | ||
revalidate() | ||
} | ||
}) | ||
|
||
backedLayer.addHierarchyListener { | ||
if (it.changeFlags and HierarchyEvent.SHOWING_CHANGED.toLong() != 0L) { | ||
checkShowing() | ||
|
@@ -143,7 +155,7 @@ actual open class SkiaLayer internal constructor( | |
addPropertyChangeListener("graphicsContextScaleTransform") { | ||
Logger.debug { "graphicsContextScaleTransform changed for $this" } | ||
latestReceivedGraphicsContextScaleTransform = it.newValue as AffineTransform | ||
redrawer?.syncSize() | ||
revalidate() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed because layout depends on the scale inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds like it could bring back https://youtrack.jetbrains.com/issue/CMP-6550/Resizing-the-window-using-WindowState-breaks-the-UI Is there a pure Compose reproducer I can try out? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A pure Compose reproducer is:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked https://youtrack.jetbrains.com/issue/CMP-6550/Resizing-the-window-using-WindowState-breaks-the-UI with this Skiko, it doesn't reproduce (it reproduces in 1.7.0-beta02 on my machine) |
||
notifyChange(PropertyKind.ContentScale) | ||
|
||
// Workaround for JBR-5259 | ||
|
@@ -191,7 +203,7 @@ actual open class SkiaLayer internal constructor( | |
redrawer?.setVisible(isShowing) | ||
} | ||
if (isShowing) { | ||
redrawer?.syncSize() | ||
redrawer?.syncBounds() | ||
repaint() | ||
} | ||
} | ||
|
@@ -252,7 +264,7 @@ actual open class SkiaLayer internal constructor( | |
private val redrawerManager = RedrawerManager<Redrawer>(properties.renderApi) { renderApi, oldRedrawer -> | ||
oldRedrawer?.dispose() | ||
val newRedrawer = renderFactory.createRedrawer(this, renderApi, analytics, properties) | ||
newRedrawer.syncSize() | ||
newRedrawer.syncBounds() | ||
newRedrawer | ||
} | ||
|
||
|
@@ -316,12 +328,11 @@ actual open class SkiaLayer internal constructor( | |
|
||
override fun doLayout() { | ||
Logger.debug { "doLayout on $this" } | ||
backedLayer.setBounds(0, 0, roundSize(width), roundSize(height)) | ||
backedLayer.setBounds(0, 0, adjustSizeToContentScale(width), adjustSizeToContentScale(height)) | ||
backedLayer.validate() | ||
redrawer?.syncSize() | ||
redrawer?.syncBounds() | ||
} | ||
|
||
|
||
override fun paint(g: java.awt.Graphics) { | ||
Logger.debug { "Paint called on: $this" } | ||
checkContentScale() | ||
|
@@ -338,7 +349,7 @@ actual open class SkiaLayer internal constructor( | |
// Please note that calling redraw during layout might break software renderers, | ||
// so applying this fix only for Direct3D case. | ||
if (renderApi == GraphicsApi.DIRECT3D && isShowing) { | ||
redrawer?.syncSize() | ||
redrawer?.syncBounds() | ||
tryRedrawImmediately() | ||
} | ||
} | ||
|
@@ -579,16 +590,15 @@ actual open class SkiaLayer internal constructor( | |
} | ||
} | ||
|
||
private fun roundSize(value: Int): Int { | ||
var rounded = value * contentScale | ||
val diff = rounded - rounded.toInt() | ||
private fun adjustSizeToContentScale(value: Int): Int { | ||
val scaled = value * contentScale | ||
val diff = scaled - scaled.toInt() | ||
// We check values close to 0.5 and edit the size to avoid white lines glitch | ||
if (diff > 0.4f && diff < 0.6f) { | ||
rounded = value + 1f | ||
return if (diff > 0.4f && diff < 0.6f) { | ||
(value + 1f).toInt() | ||
} else { | ||
rounded = value.toFloat() | ||
value.toFloat().toInt() | ||
} | ||
return rounded.toInt() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change looks valid (i.e. doesn't change semantics), but this is a really weird function. I understand neither what it does nor why it does it. Just purely mechanical, minor, suggestions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
} | ||
|
||
fun requestNativeFocusOnAccessible(accessible: Accessible?) { | ||
|
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.
Why not
redrawer?.syncBounds()
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.
revalidate
sounds more expensive...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 tried, but we have glitches on resize. It seems we need to schedule it to the layout phase, to keep layouts in sync.
I checked the performance when we move the window, it doesn't decrease. When we call
revalidate
, it schedules relayout of only SkiaLayer + one childjava.awt.Canvas
. They don't have other children (aSwingPanel
in Compose is added to a sibling container)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 guess I'm just not sure I understand why revalidation is needed here... Only the (absolute) position of the layer changes, not the size, so layout should not change.
Maybe
SwingUtilities.invokeLater { redrawer?.syncBounds() }
?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.
We can't say that
invokeLater
is in sync with layout.Actually I figured why there were resize glitches.
addAncestorListener
receives events asynchronously. I replaced it byaddHierarchyBoundsListener
which is called synchronously on the parent layout. We don't needrevalidate
then.Also, I looked at the
revalidate
doc, and you are right, it is in't performant, as it actually revalidates the whole tree back to the root.So, I removed it from the other place, I am not confident now to fix another very small issue (recalling
adjustSizeToContentScale
on content scale change).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.
Sorry, wait a minute. I tested it with a wrong version. Glitches are back. Investigating
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 returned to
revalidate
.I looked at the documentation:
https://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#revalidate()
validateRoot
, but it doesn't validate all children, just what wasinvalidated
validate
is expensive, but it is deferred, and actually is already called on every parent size change or when a component is added/removedI don't want to user
invokeLater
because:dispose
invokeLater
syncBounds
too often - we called it even if it haven't performeddoLayout
yet, and sosyncBounds
was based on incorrect size.