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

Add test for swing interop #737

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import javax.swing.SwingUtilities.isEventDispatchThread
*/
@Suppress("unused") // used in Compose Multiplatform
@ExperimentalSkikoApi
open class SkiaSwingLayer(
open class SkiaSwingLayer internal constructor(
skikoView: SkikoView,
private val properties: SkiaLayerProperties,
analytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
) : JComponent() {
internal companion object {
Expand All @@ -31,8 +32,6 @@ open class SkiaSwingLayer(
}
}

private val properties = SkiaLayerProperties()

private var isInitialized = false

@Volatile
Expand Down Expand Up @@ -73,6 +72,11 @@ open class SkiaSwingLayer(
val renderApi: GraphicsApi
get() = redrawerManager.renderApi

constructor(
skikoView: SkikoView,
analytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
) : this(skikoView, SkiaLayerProperties(), analytics)

init {
isOpaque = false
layout = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,4 +835,4 @@ class SkiaLayerTest {
}
}

private fun JFrame.close() = dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING))
internal fun JFrame.close() = dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING))
219 changes: 219 additions & 0 deletions skiko/src/awtTest/kotlin/org/jetbrains/skiko/SkiaSwingLayerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package org.jetbrains.skiko

import kotlinx.coroutines.delay
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect
import org.jetbrains.skiko.swing.SkiaSwingLayer
import org.jetbrains.skiko.util.ScreenshotTestRule
import org.jetbrains.skiko.util.uiTest
import org.junit.Rule
import java.awt.*
import javax.swing.*
import javax.swing.JLayeredPane.DEFAULT_LAYER
import javax.swing.JLayeredPane.POPUP_LAYER
import kotlin.test.Test

@OptIn(ExperimentalSkikoApi::class)
class SkiaSwingLayerTest {

@get:Rule
val screenshots = ScreenshotTestRule()

@Test
fun `overlapped popup`() = uiTest {
Walingar marked this conversation as resolved.
Show resolved Hide resolved
val skikoView = fillSkikoView()
val skiaLayer = SkiaSwingLayer(skikoView, SkiaLayerProperties().copy(renderApi = renderApi)).apply {
setBounds(0, 0, 200, 200)
}

singleWindowTest(skiaLayer) { contentPane ->
val popup = object : JComponent() {
init {
isOpaque = false
setBounds(50, 50, 50, 50)
}

override fun paintComponent(g: Graphics?) {
val scratchGraphics = g?.create() as? Graphics2D ?: return
try {
scratchGraphics.color = Color(0, 0, 0, 50)
scratchGraphics.fillRect(0, 0, 50, 50)

scratchGraphics.color = Color.GREEN
scratchGraphics.fillRoundRect(5, 5, 40, 40, 16, 16)
} finally {
scratchGraphics.dispose()
}
}
}

val layeredPane = JLayeredPane()
layeredPane.add(skiaLayer)
layeredPane.add(popup)

layeredPane.setLayer(skiaLayer, DEFAULT_LAYER)
layeredPane.setLayer(popup, POPUP_LAYER)

contentPane.add(layeredPane)

delay(1000)
checkScreenshot()
}
}

@Test
fun `multiple layers`() = uiTest {
val skikoView1 = fillSkikoView(color = Color.CYAN)
val skiaLayer1 = SkiaSwingLayer(skikoView1, SkiaLayerProperties().copy(renderApi = renderApi)).apply {
setBounds(0, 0, 200, 200)
}
val skikoView2 = fillSkikoView(color = Color.GREEN)
val skiaLayer2 = SkiaSwingLayer(skikoView2, SkiaLayerProperties().copy(renderApi = renderApi)).apply {
setBounds(50, 50, 200, 200)
}

val skikoView3 = fillSkikoView(color = Color.RED)
val skiaLayer3 = SkiaSwingLayer(skikoView3, SkiaLayerProperties().copy(renderApi = renderApi)).apply {
setBounds(100, 100, 25, 25)
}

singleWindowTest(
dispose = {
skiaLayer1.dispose()
skiaLayer2.dispose()
skiaLayer3.dispose()
}
) { contentPane ->
val layeredPane = JLayeredPane()

layeredPane.add(skiaLayer1)
layeredPane.add(skiaLayer2)
layeredPane.add(skiaLayer3)

layeredPane.setLayer(skiaLayer1, 0)
layeredPane.setLayer(skiaLayer2, 1)
layeredPane.setLayer(skiaLayer3, 2)

contentPane.add(layeredPane)

delay(1000)
checkScreenshot()
}
}

@Test
fun `layer resize`() = uiTest {
val skikoView = fillSkikoView()
val skiaLayer = SkiaSwingLayer(skikoView, SkiaLayerProperties().copy(renderApi = renderApi))

singleWindowTest(skiaLayer) { contentPane ->
val splitPane = JSplitPane()
splitPane.leftComponent = skiaLayer
splitPane.rightComponent = JPanel()

splitPane.dividerLocation = 50

contentPane.add(splitPane)

delay(1000)
checkScreenshot("position1")

splitPane.dividerLocation = 100
delay(1000)
checkScreenshot("position2")
}
}

@Test
fun `overlapped skia layer`() = uiTest {
val skikoView = object : SkikoView {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
val rect = Rect(0f, 0f, width.toFloat() - 10f, height.toFloat() - 10f)
canvas.drawRect(rect, Paint().apply {
this.color = Color.CYAN.rgb
})

canvas.drawRectShadow(
rect,
dx = 3f, dy = 3f,
blur = 0.5f,
Color(0, 0, 0, 50).rgb
)
}
}
val skiaLayer = SkiaSwingLayer(skikoView, SkiaLayerProperties().copy(renderApi = renderApi)).apply {
setBounds(50, 50, 50, 50)
}

singleWindowTest(skiaLayer) { contentPane ->
val panel = JPanel().apply {
background = Color.GREEN
setBounds(0, 0, 200, 200)
}

val layeredPane = JLayeredPane()
layeredPane.add(panel)
layeredPane.add(skiaLayer)

layeredPane.setLayer(panel, DEFAULT_LAYER)
layeredPane.setLayer(skiaLayer, POPUP_LAYER)

contentPane.add(layeredPane)

delay(1000)
checkScreenshot()
}
}

private inline fun singleWindowTest(
layer: SkiaSwingLayer,
block: SingleWindowTestScope.(contentPane: Container) -> Unit
) {
singleWindowTest(
dispose = {
layer.dispose()
},
block
)
}

private inline fun singleWindowTest(
crossinline dispose: () -> Unit,
block: SingleWindowTestScope.(contentPane: Container) -> Unit
) {
val window = object : JFrame() {
override fun dispose() {
dispose()
super.dispose()
}
}

try {
window.isUndecorated = true
window.size = Dimension(400, 400)
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
window.isVisible = true
val scope = object : SingleWindowTestScope {
override fun checkScreenshot(id: String) {
screenshots.assert(window.bounds, id)
}
}
scope.block(window.contentPane)
} finally {
window.close()
}
}

private fun fillSkikoView(color: Color = Color.RED): SkikoView = object : SkikoView {
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
canvas.drawRect(Rect(0f, 0f, width.toFloat(), height.toFloat()), Paint().apply {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Worth add a semi-transparent green background around the rect, to check blending (to the Swing rect as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it is worth adding it only for swing here. Will do

this.color = color.rgb
})
}
}

private interface SingleWindowTestScope {
fun checkScreenshot(id: String = "")
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.