Unverified Commit 94ef0533 authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

JVM SkiaLayer API (#281)

parent 756df5ab
......@@ -31,13 +31,40 @@ i.e. something like this
else -> error("Unsupported arch: $osArch")
}
val version = "0.4.12"
val version = "0.5.2"
val target = "${targetOs}-${targetArch}"
dependencies {
implementation("org.jetbrains.skiko:skiko-jvm-runtime-$target:$version")
}
```
Simple example for Kotlin/JVM
```kotlin
fun main() {
val skiaLayer = SkiaLayer()
skiaLayer.skikoView = GenericSkikoView(skiaLayer, object : SkikoView {
val paint = Paint().apply {
color = Color.RED
}
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
canvas.clear(Color.CYAN)
val ts = nanoTime / 5_000_000
canvas.drawCircle( (ts % width).toFloat(), (ts % height).toFloat(), 20f, paint )
}
})
SwingUtilities.invokeLater {
val window = JFrame("Skiko example").apply {
defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
preferredSize = Dimension(800, 600)
}
skiaLayer.attachTo(window.contentPane)
skiaLayer.needRedraw()
window.pack()
window.isVisible = true
}
}
```
To use latest development snapshot use version `0.0.0-SNAPSHOT`.
## Building JVM bindings
......
plugins {
kotlin("multiplatform") version "1.5.31"
kotlin("multiplatform") version "1.6.0-RC"
}
val coroutinesVersion = "1.5.2"
......@@ -60,6 +60,12 @@ kotlin {
targets.add(iosArm64())
}
jvm {
compilations.all {
kotlinOptions.jvmTarget = "11"
}
}
js(IR) {
browser()
binaries.executable()
......@@ -92,6 +98,13 @@ kotlin {
dependsOn(commonMain)
}
val jvmMain by getting {
dependsOn(commonMain)
dependencies {
implementation("org.jetbrains.skiko:skiko-jvm-runtime-$hostOs-$hostArch:$version")
}
}
val jsMain by getting {
dependsOn(commonMain)
resources.setSrcDirs(resources.srcDirs)
......@@ -150,7 +163,7 @@ project.tasks.register<Exec>("runIosSim") {
}
}
project.tasks.register<Exec>("run") {
project.tasks.register<Exec>("runNative") {
workingDir = project.buildDir
val binTask = project.tasks.named("linkDebugExecutable${hostOs.capitalize()}${hostArch.capitalize()}")
dependsOn(binTask)
......@@ -163,6 +176,26 @@ project.tasks.register<Exec>("run") {
}
}
project.tasks.register<JavaExec>("runJvm") {
val kotlinTask = project.tasks.named("compileKotlinJvm")
dependsOn(kotlinTask)
systemProperty("skiko.fps.enabled", "true")
systemProperty("skiko.linux.autodpi", "true")
systemProperty("skiko.hardwareInfo.enabled", "true")
systemProperty("skiko.win.exception.logger.enabled", "true")
systemProperty("skiko.win.exception.handler.enabled", "true")
jvmArgs?.add("-ea")
System.getProperties().entries
.associate {
(it.key as String) to (it.value as String)
}
.filterKeys { it.startsWith("skiko.") }
.forEach { systemProperty(it.key, it.value) }
mainClass.set("org.jetbrains.skiko.sample.App_jvmKt")
classpath(kotlinTask.get().outputs)
classpath(kotlin.jvm().compilations["main"].runtimeDependencyFiles)
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>().configureEach {
dependsOn(unzipTask)
}
......
......@@ -84,9 +84,10 @@ class BouncingBalls: SkikoView {
Color4f(0f, 1f, 1f, 0.8f).asPaint()
)).toMutableList()
override fun onRender(canvas: Canvas, width: Int, height: Int, currentTimestamp: Long) {
val dtime = (currentTimestamp - prevTimestamp)
prevTimestamp = currentTimestamp
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
canvas.clear(-1)
val dtime = (nanoTime - prevTimestamp)
prevTimestamp = nanoTime
data.forEach { (ball, paint) ->
ball.recalculate(width, height, dtime.toFloat())
......@@ -112,7 +113,7 @@ class BouncingBalls: SkikoView {
data.removeLast()
}
// To avoid log spamming
//if (event.kind != SkikoMouseEventKind.MOVE)
if (event.kind != SkikoPointerEventKind.MOVE)
println("onMouse: $event")
}
}
\ No newline at end of file
......@@ -13,7 +13,7 @@ fun main() {
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
canvas.setAttribute("tabindex", "0")
skiaLayer.setCanvas(canvas)
skiaLayer.attachTo(canvas)
skiaLayer.needRedraw()
}
}
package org.jetbrains.skiko.sample
import org.jetbrains.skiko.*
import java.awt.Dimension
import javax.swing.*
fun main() {
val skiaLayer = SkiaLayer()
skiaLayer.skikoView = GenericSkikoView(skiaLayer, BouncingBalls())
SwingUtilities.invokeLater {
val window = JFrame("Skiko example").apply {
defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
preferredSize = Dimension(800, 600)
}
skiaLayer.attachTo(window.contentPane)
skiaLayer.needRedraw()
window.pack()
window.isVisible = true
}
}
......@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
plugins {
kotlin("multiplatform") version "1.5.31" // "1.6.0-M1"
kotlin("multiplatform") version "1.5.31"
`maven-publish`
id("org.gradle.crypto.checksum") version "1.1.0"
id("de.undercouch.download") version "4.1.1"
......@@ -202,8 +202,13 @@ val linkWasm = tasks.register<LinkSkikoWasmTask>("linkWasm") {
val skiaBinSubdir = "out/${buildType.id}-${targetOs.id}-${targetArch.id}"
internal val Project.isInIdea: Boolean
get() {
return System.getProperty("idea.active")?.toBoolean() == true
}
val Project.supportNative: Boolean
get() = properties.get("skiko.native.enabled") == "true"
get() = (properties.get("skiko.native.enabled") == "true") || isInIdea
val Project.supportWasm: Boolean
get() = properties.get("skiko.wasm.enabled") == "true"
......
......@@ -470,7 +470,6 @@ SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Font__1nGetMetrics
#endif
SKIKO_EXPORT KFloat org_jetbrains_skia_Font__1nGetSpacing
(KNativePointer ptr, KShort* glyphsArr) {
TODO("implement org_jetbrains_skia_Font__1nGetSpacing");
......
......@@ -10,7 +10,10 @@ expect open class SkiaLayer {
var skikoView: SkikoView?
// Actual type of attach container is platform-specific.
fun attachTo(container: Any)
fun needRedraw()
}
internal class PictureHolder(val instance: Picture, val width: Int, val height: Int)
......@@ -49,9 +49,11 @@ actual open class SkiaLayer(
lateinit var view: UIView
// We need to keep reference to controller as Objective-C will only keep weak reference here.
lateinit private var controller: NSObject
fun initLayer(viewController: SkikoViewController) {
this.view = viewController.view
actual fun attachTo(container: Any) {
attachTo(container as UIView)
}
fun attachTo(view: UIView) {
this.view = view
// See https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events?language=objc
controller = object : NSObject() {
......
......@@ -42,6 +42,6 @@ class SkikoViewController : UIViewController {
skikoView = appFactory(this)
}
view.setFrame(CGRectMake(0.0, 0.0, width, height))
layer.initLayer(this)
layer.attachTo(this.view)
}
}
......@@ -47,7 +47,11 @@ actual open class SkiaLayer(properties: SkiaLayerProperties = makeDefaultSkiaLay
)
}
fun setCanvas(htmlCanvas: HTMLCanvasElement) {
actual fun attachTo(container: Any) {
attachTo(container as HTMLCanvasElement)
}
fun attachTo(htmlCanvas: HTMLCanvasElement) {
state = object: CanvasRenderer(htmlCanvas) {
override fun drawFrame(currentTimestamp: Double) {
// currentTimestamp is in milliseconds.
......
......@@ -19,6 +19,7 @@ import java.awt.event.*
import java.awt.im.InputMethodRequests
import java.util.concurrent.CancellationException
import javax.accessibility.Accessible
import javax.swing.JComponent
import javax.swing.JPanel
import javax.swing.SwingUtilities.isEventDispatchThread
......@@ -124,6 +125,69 @@ actual open class SkiaLayer internal constructor(
actual var skikoView: SkikoView? = null
actual fun attachTo(container: Any) {
attachTo(container as JComponent)
}
fun attachTo(jComponent: JComponent) {
jComponent.add(this)
backedLayer.addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent?) {
e!!
skikoView?.onPointerEvent(
SkikoPointerEvent(e.x.toDouble(), e.y.toDouble(),
if (e.button == 1) SkikoMouseButtons.LEFT else SkikoMouseButtons.RIGHT,
SkikoPointerEventKind.DOWN,
e
)
)
}
override fun mouseReleased(e: MouseEvent?) {
e!!
skikoView?.onPointerEvent(
SkikoPointerEvent(e.x.toDouble(), e.y.toDouble(),
if (e.button == 1) SkikoMouseButtons.LEFT else SkikoMouseButtons.RIGHT,
SkikoPointerEventKind.UP,
e
)
)
}
})
backedLayer.addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(e: MouseEvent?) {
e!!
skikoView?.onPointerEvent(
SkikoPointerEvent(e.x.toDouble(), e.y.toDouble(),
SkikoMouseButtons.NONE,
SkikoPointerEventKind.MOVE,
e
)
)
}
})
backedLayer.addKeyListener(object : KeyAdapter() {
override fun keyPressed(e: KeyEvent?) {
e!!
skikoView?.onKeyboardEvent(
SkikoKeyboardEvent(e.keyCode,
SkikoKeyboardEventKind.DOWN,
e
)
)
}
override fun keyReleased(e: KeyEvent?) {
e!!
skikoView?.onKeyboardEvent(
SkikoKeyboardEvent(
e.keyCode,
SkikoKeyboardEventKind.UP,
e
)
)
}
})
}
val clipComponents = mutableListOf<ClipRectangle>()
@Volatile
......
......@@ -2,6 +2,7 @@ package org.jetbrains.skiko
import javax.swing.JFrame
@Deprecated("Will be removed soon")
open class SkiaWindow(
properties: SkiaLayerProperties = makeDefaultSkiaLayerProperties(),
layerFactory: () -> SkiaLayer = { SkiaLayer(properties) }
......@@ -24,6 +25,12 @@ open class SkiaWindow(
}
}
fun SkiaLayer.disableTitleBar() {
backedLayer.useDrawingSurfacePlatformInfo {
platformOperations.disableTitleBar(it)
}
}
fun orderEmojiAndSymbolsPopup() {
platformOperations.orderEmojiAndSymbolsPopup()
}
\ No newline at end of file
......@@ -15,6 +15,9 @@ actual open class SkiaLayer(properties: SkiaLayerProperties) {
actual fun needRedraw() {
TODO("unimplemented")
}
actual fun attachTo(container: Any) {
TODO("unimplemented")
}
actual var skikoView: SkikoView? = null
}
......
......@@ -60,8 +60,10 @@ actual open class SkiaLayer(
private fun eventToKeyboard(event: NSEvent, kind: SkikoKeyboardEventKind): SkikoKeyboardEvent {
return SkikoKeyboardEvent(event.keyCode.toInt(), kind, event)
}
fun initLayer(window: NSWindow) {
actual fun attachTo(container: Any) {
attachTo(container as NSWindow)
}
fun attachTo(window: NSWindow) {
val (width, height) = window.contentLayoutRect.useContents {
this.size.width to this.size.height
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment