Unverified Commit f8968123 authored by Oleksandr Karpovich's avatar Oleksandr Karpovich Committed by GitHub

Refactor interop with js in k/js and k/wasm source sets (#843)

* Refactor interop with js in k/js and k/wasm source sets

* Make event conversion functions internal

The event conversion functions in the 'Convertors.js.kt' and 'Convertors.wasm.kt' files have been updated from public to internal.

* Disable local maven repository

* PR fixes

---------
Co-authored-by: 's avatarOleksandr.Karpovich <oleksandr.karpovich@jetbrains.com>
parent 70282bd6
kotlin.code.style=official
kotlin.version=1.9.20
skiko.composite.build=1
\ No newline at end of file
kotlin.version=1.9.21
#skiko.composite.build=1
\ No newline at end of file
package org.jetbrains.skiko.sample.js
import kotlinx.browser.document
import org.jetbrains.skiko.wasm.onWasmReady
fun main() {
onWasmReady(::runApp)
}
\ No newline at end of file
}
actual fun findElementById(id: String): Any? = document.getElementById(id)
\ No newline at end of file
package org.jetbrains.skiko.sample.js
import kotlinx.browser.document
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect
import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkikoView
import org.w3c.dom.HTMLCanvasElement
private class DemoApp: SkikoView {
private val paint = Paint()
......@@ -25,7 +23,7 @@ private class DemoApp: SkikoView {
internal fun runApp() {
for (index in 1 .. 3) {
val skiaLayer = SkiaLayer()
val canvas = document.getElementById("c$index") as HTMLCanvasElement
val canvas = findElementById("c$index")!!
val app = if (index == 3) {
DemoApp()
} else {
......@@ -35,4 +33,9 @@ internal fun runApp() {
skiaLayer.attachTo(canvas)
skiaLayer.needRedraw()
}
}
\ No newline at end of file
}
// Note: despite the actual implementation looks identical, it can't be common - there is no W3C common API
// return type is Any because k/js and k/wasm don't have a common type for HTML elements
expect fun findElementById(id: String): Any?
\ No newline at end of file
package org.jetbrains.skiko.sample.js
import kotlinx.browser.document
fun main() {
runApp()
}
\ No newline at end of file
}
actual fun findElementById(id: String): Any? = document.getElementById(id)
\ No newline at end of file
......@@ -22,6 +22,8 @@ repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
// mavenLocal()
}
val osName = System.getProperty("os.name")
......
kotlin.code.style=official
org.gradle.jvmargs=-Xmx3G -XX:MaxMetaspaceSize=512m
kotlin.version=1.9.20
kotlin.version=1.9.21
#skiko.composite.build=1
\ No newline at end of file
package org.jetbrains.skiko.sample
import kotlinx.browser.document
import kotlinx.browser.window
import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.wasm.onWasmReady
import org.w3c.dom.HTMLCanvasElement
fun main() {
window.addEventListener("DOMContentLoaded", {
onWasmReady(::runApp)
})
}
internal fun runApp() {
val skiaLayer = SkiaLayer()
skiaLayer.onContentScaleChanged = { scale -> println(scale) }
val game = JsClocks(skiaLayer)
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
canvas.setAttribute("tabindex", "0")
skiaLayer.attachTo(canvas)
skiaLayer.needRedraw()
}
\ No newline at end of file
package org.jetbrains.skiko.sample
import kotlinx.browser.document
import org.jetbrains.skiko.*
import org.w3c.dom.HTMLCanvasElement
internal fun runApp() {
val skiaLayer = SkiaLayer()
onContentScaleChanged = { scale -> println(scale) }
val game = JsClocks(skiaLayer)
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
canvas.setAttribute("tabindex", "0")
skiaLayer.attachTo(canvas)
skiaLayer.needRedraw()
}
\ No newline at end of file
package org.jetbrains.skiko.sample
import kotlinx.browser.document
import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer
import org.w3c.dom.HTMLCanvasElement
fun main() {
runApp()
}
internal fun runApp() {
val skiaLayer = SkiaLayer()
skiaLayer.onContentScaleChanged = { scale -> println(scale) }
val game = JsClocks(skiaLayer)
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
canvas.setAttribute("tabindex", "0")
skiaLayer.attachTo(canvas)
skiaLayer.needRedraw()
}
\ No newline at end of file
......@@ -542,14 +542,6 @@ project.tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>().config
)
}
if (supportJs && supportWasm) {
project.tasks.whenTaskAdded {
if (name == "compileJsWasmMainKotlinMetadata") {
enabled = false
}
}
}
tasks.findByName("publishSkikoWasmRuntimePublicationToComposeRepoRepository")
?.dependsOn("publishWasmJsPublicationToComposeRepoRepository")
......
package org.jetbrains.skiko
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.HTMLElement
import org.w3c.dom.events.UIEvent
internal actual fun getEventTimestamp(e: UIEvent): Long {
internal fun getEventTimestamp(e: UIEvent): Long {
return e.timeStamp.toLong()
}
\ No newline at end of file
}
internal actual fun CursorManager_setCursor(component: Any, cursor: Cursor) {
(component as? HTMLElement)?.style?.cursor = cursor
}
internal actual fun CursorManager_getCursor(component: Any): Cursor? {
return (component as? HTMLElement)?.style?.cursor
}
internal actual fun getNavigatorInfo(): String =
js("navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform") as String
\ No newline at end of file
package org.jetbrains.skiko
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.wasm.GL
internal actual fun makeGLContextCurrent(contextPointer: NativePointer) {
GL.makeContextCurrent(contextPointer)
}
\ No newline at end of file
package org.jetbrains.skiko
import org.w3c.dom.events.*
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.events.WheelEvent
private val SPECIAL_KEYS = setOf(
"Unidentified",
"Alt",
"AltGraph",
"Backspace",
"CapsLock",
"Control",
"Fn",
"FnLock",
"Hyper",
"Meta",
"NumLock",
"ScrollLock",
"Shift",
"Super",
"Symbol",
"SymbolLock",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"F13",
"F14",
"F15",
"F16",
"F17",
"F18",
"F19",
"F20",
"F21",
"F22",
"ArrowLeft",
"ArrowUp",
"ArrowRight",
"ArrowDown",
"Help",
"Home",
"Delete",
"End",
"PageUp",
"PageDown",
"Escape",
"Clear",
"Clear"
)
internal expect fun getEventTimestamp(e: UIEvent): Long
fun toSkikoEvent(
internal fun toSkikoEvent(
event: MouseEvent,
kind: SkikoPointerEventKind
): SkikoPointerEvent {
......@@ -74,7 +20,7 @@ fun toSkikoEvent(
)
}
fun toSkikoDragEvent(
internal fun toSkikoDragEvent(
event: MouseEvent
): SkikoPointerEvent {
return SkikoPointerEvent(
......@@ -89,7 +35,7 @@ fun toSkikoDragEvent(
)
}
fun toSkikoTypeEvent(
internal fun toSkikoTypeEvent(
character: String,
event: KeyboardEvent?,
): SkikoInputEvent? {
......@@ -114,7 +60,7 @@ fun toSkikoTypeEvent(
}
fun toSkikoEvent(
internal fun toSkikoEvent(
event: KeyboardEvent,
kind: SkikoKeyboardEventKind
): SkikoKeyboardEvent {
......@@ -127,7 +73,7 @@ fun toSkikoEvent(
)
}
fun toSkikoScrollEvent(
internal fun toSkikoScrollEvent(
event: WheelEvent,
): SkikoPointerEvent {
return SkikoPointerEvent(
......@@ -217,7 +163,7 @@ private fun toSkikoKey(event: KeyboardEvent): Int {
key == SkikoKey.KEY_LEFT_SHIFT.platformKeyCode ||
key == SkikoKey.KEY_LEFT_META.platformKeyCode
)
key = key.or(0x80000000.toInt())
key = key.or(0x80000000.toInt())
}
return key
}
\ No newline at end of file
package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.events.WheelEvent
internal actual fun SkiaLayer.bindCanvasEventsToSkikoView(
canvas: HTMLCanvasElement
) {
val htmlCanvas = canvas.unsafeCast<org.w3c.dom.HTMLCanvasElement>()
htmlCanvas.addEventListener("mousedown", { event ->
event as MouseEvent
isPointerPressed = true
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.DOWN))
})
htmlCanvas.addEventListener("mouseup", { event ->
event as MouseEvent
isPointerPressed = false
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.UP))
})
htmlCanvas.addEventListener("mousemove", { event ->
event as MouseEvent
if (isPointerPressed) {
skikoView?.onPointerEvent(toSkikoDragEvent(event))
} else {
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.MOVE))
}
})
htmlCanvas.addEventListener("wheel", { event ->
event as WheelEvent
skikoView?.onPointerEvent(toSkikoScrollEvent(event))
})
htmlCanvas.addEventListener("contextmenu", { event ->
event.preventDefault()
})
htmlCanvas.addEventListener("keydown", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN))
toSkikoTypeEvent(event.key, event)?.let { inputEvent ->
skikoView?.input?.onInputEvent(inputEvent)
}
})
htmlCanvas.addEventListener("keyup", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP))
})
}
actual typealias SkikoGesturePlatformEvent = Any
actual typealias SkikoPlatformInputEvent = KeyboardEvent
actual typealias SkikoPlatformKeyboardEvent = KeyboardEvent
// MouseEvent is base class of PointerEvent
actual typealias SkikoPlatformPointerEvent = MouseEvent
internal actual fun SkiaLayer.setOnChangeScaleNotifier() {
state?.initCanvas(desiredWidth, desiredHeight, contentScale, this.pixelGeometry)
window.matchMedia("(resolution: ${contentScale}dppx)")
.addEventListener("change", { setOnChangeScaleNotifier() }, true)
onContentScaleChanged?.invoke(contentScale)
}
\ No newline at end of file
package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer
import org.w3c.dom.HTMLCanvasElement
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import kotlin.js.*
/**
......@@ -40,7 +40,6 @@ internal external object GL : GLInterface {
override fun makeContextCurrent(contextPointer: NativePointer): Boolean = definedExternally
}
internal actual fun createWebGLContext(canvas: HTMLCanvasElement, attr: ContextAttributes?): NativePointer {
val contextAttributes = object : ContextAttributes {
override val alpha = attr?.alpha ?: 1
......
package org.jetbrains.skia
import kotlinx.browser.window
import org.jetbrains.skiko.w3c.window
internal actual fun <R> commonSynchronized(lock: Any, block: () -> R) {
block()
......
package org.jetbrains.skiko
import org.w3c.dom.HTMLElement
import org.jetbrains.skiko.w3c.window
internal actual inline fun <R> maybeSynchronized(lock: Any, block: () -> R): R =
block()
actual fun currentNanoTime(): Long = (kotlinx.browser.window.performance.now() * 1_000_000).toLong()
actual fun currentNanoTime(): Long = (window.performance.now() * 1_000_000).toLong()
internal actual fun URIHandler_openUri(uri: String) {
kotlinx.browser.window.open(uri, target = "_blank")
window.open(uri, target = "_blank")
}
internal actual fun ClipboardManager_setText(text: String) {
kotlinx.browser.window.navigator.clipboard.writeText(text)
window.navigator.clipboard.writeText(text)
}
internal actual fun ClipboardManager_getText(): String? {
......@@ -25,20 +25,6 @@ internal actual fun ClipboardManager_hasText(): Boolean = !ClipboardManager_getT
actual typealias Cursor = String
internal actual fun CursorManager_setCursor(component: Any, cursor: Cursor) {
if (component is HTMLElement) {
component.style.cursor = cursor
}
}
internal actual fun CursorManager_getCursor(component: Any): Cursor? {
return if (component is HTMLElement) {
component.style.cursor
} else {
null
}
}
internal actual fun getCursorById(id: PredefinedCursorsId): Cursor =
when (id) {
PredefinedCursorsId.DEFAULT -> "default"
......
package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skia.*
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import org.jetbrains.skiko.w3c.window
import org.jetbrains.skiko.wasm.createWebGLContext
import org.jetbrains.skiko.wasm.GL
import org.w3c.dom.HTMLCanvasElement
/**
* CanvasRenderer takes an [HTMLCanvasElement] instance and initializes
......@@ -13,7 +13,9 @@ import org.w3c.dom.HTMLCanvasElement
* After initialization [needRedraw] can be used to schedule a call to [drawFrame].
* [drawFrame] has to be implemented to perform the actual drawing on [canvas].
*/
abstract class CanvasRenderer constructor(val htmlCanvas: HTMLCanvasElement) {
internal abstract class CanvasRenderer(
private val htmlCanvas: HTMLCanvasElement
) {
private val contextPointer = createWebGLContext(htmlCanvas)
private val context: DirectContext
private var surface: Surface? = null
......@@ -39,7 +41,7 @@ abstract class CanvasRenderer constructor(val htmlCanvas: HTMLCanvasElement) {
get() = htmlCanvas.height
init {
GL.makeContextCurrent(contextPointer)
makeGLContextCurrent(contextPointer)
context = DirectContext.makeGL()
}
......@@ -93,7 +95,7 @@ abstract class CanvasRenderer constructor(val htmlCanvas: HTMLCanvasElement) {
redrawScheduled = true
window.requestAnimationFrame { timestamp ->
redrawScheduled = false
GL.makeContextCurrent(contextPointer)
makeGLContextCurrent(contextPointer)
// `clear` and `resetMatrix` make canvas not accumulate previous effects
canvas?.clear(Color.WHITE)
canvas?.resetMatrix()
......@@ -103,3 +105,5 @@ abstract class CanvasRenderer constructor(val htmlCanvas: HTMLCanvasElement) {
}
}
}
internal expect fun makeGLContextCurrent(contextPointer: NativePointer)
\ No newline at end of file
package org.jetbrains.skiko
internal val SPECIAL_KEYS = setOf(
"Unidentified",
"Alt",
"AltGraph",
"Backspace",
"CapsLock",
"Control",
"Fn",
"FnLock",
"Hyper",
"Meta",
"NumLock",
"ScrollLock",
"Shift",
"Super",
"Symbol",
"SymbolLock",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"F13",
"F14",
"F15",
"F16",
"F17",
"F18",
"F19",
"F20",
"F21",
"F22",
"ArrowLeft",
"ArrowUp",
"ArrowRight",
"ArrowDown",
"Help",
"Home",
"Delete",
"End",
"PageUp",
"PageDown",
"Escape",
"Clear",
"Clear"
)
\ No newline at end of file
package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skiko.w3c.window
actual val hostOs: OS = detectHostOs()
......@@ -22,9 +22,7 @@ actual val kotlinBackend: KotlinBackend
* See https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/platform - new API,
* but not supported in all browsers
*/
private fun getNavigatorInfo(): String =
js("navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform") //as String // k/wasm requires only 1 expression in a function calling `js`
internal expect fun getNavigatorInfo(): String
/**
* In a browser, user platform can be obtained from different places:
......
package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.PixelGeometry
import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.events.InputEvent
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.events.WheelEvent
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import org.jetbrains.skiko.w3c.window
/**
* Provides a way to render the content and to receive the input events.
......@@ -17,7 +13,7 @@ import org.w3c.dom.events.WheelEvent
* using [attachTo] method.
*/
actual open class SkiaLayer {
private var state: CanvasRenderer? = null
internal var state: CanvasRenderer? = null
/**
* [GraphicsApi.WEBGL] is the only supported renderApi for k/js (browser).
......@@ -72,10 +68,10 @@ actual open class SkiaLayer {
// TODO: when switch to the frame dispatcher - stop it here.
}
private var isPointerPressed = false
internal var isPointerPressed = false
private var desiredWidth = 0
private var desiredHeight = 0
internal var desiredWidth = 0
internal var desiredHeight = 0
actual val component: Any?
get() = this.htmlCanvas
......@@ -86,7 +82,7 @@ actual open class SkiaLayer {
* Initializes the [CanvasRenderer] and events listeners.
* Delegates rendering and events processing to [skikoView].
*/
fun attachTo(htmlCanvas: HTMLCanvasElement, autoDetach: Boolean = true) {
private fun attachTo(htmlCanvas: HTMLCanvasElement, autoDetach: Boolean = true) {
this.htmlCanvas = htmlCanvas
// Scale canvas to allow high DPI rendering as suggested in
......@@ -105,49 +101,7 @@ actual open class SkiaLayer {
}.apply { initCanvas(desiredWidth, desiredHeight, contentScale, pixelGeometry) }
// See https://www.w3schools.com/jsref/dom_obj_event.asp
// https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
htmlCanvas.addEventListener("mousedown", { event ->
event as MouseEvent
isPointerPressed = true
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.DOWN))
})
htmlCanvas.addEventListener("mouseup", { event ->
event as MouseEvent
isPointerPressed = false
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.UP))
})
htmlCanvas.addEventListener("mousemove", { event ->
event as MouseEvent
if (isPointerPressed) {
skikoView?.onPointerEvent(toSkikoDragEvent(event))
} else {
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.MOVE))
}
})
htmlCanvas.addEventListener("wheel", { event ->
event as WheelEvent
skikoView?.onPointerEvent(toSkikoScrollEvent(event))
})
htmlCanvas.addEventListener("contextmenu", { event ->
event.preventDefault()
})
htmlCanvas.addEventListener("keydown", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN))
toSkikoTypeEvent(event.key, event)?.let { inputEvent ->
skikoView?.input?.onInputEvent(inputEvent)
}
})
htmlCanvas.addEventListener("keyup", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP))
})
}
private fun setOnChangeScaleNotifier() {
state?.initCanvas(desiredWidth, desiredHeight, contentScale, this.pixelGeometry)
window.matchMedia("(resolution: ${contentScale}dppx)").addEventListener("change", { setOnChangeScaleNotifier() }, true)
onContentScaleChanged?.invoke(contentScale)
bindCanvasEventsToSkikoView(htmlCanvas)
}
internal actual fun draw(canvas: Canvas) {
......@@ -156,12 +110,10 @@ actual open class SkiaLayer {
actual val pixelGeometry: PixelGeometry
get() = PixelGeometry.UNKNOWN
var onContentScaleChanged: ((Float) -> Unit)? = null
}
var onContentScaleChanged: ((Float) -> Unit)? = null
actual typealias SkikoGesturePlatformEvent = Any
actual typealias SkikoPlatformInputEvent = KeyboardEvent
actual typealias SkikoPlatformKeyboardEvent = KeyboardEvent
// MouseEvent is base class of PointerEvent
actual typealias SkikoPlatformPointerEvent = MouseEvent
internal expect fun SkiaLayer.bindCanvasEventsToSkikoView(canvas: HTMLCanvasElement)
internal expect fun SkiaLayer.setOnChangeScaleNotifier()
package org.jetbrains.skiko.w3c
/**
* The purpose of this file is to facilitate shared W3C type declarations
* for use in both kotlin/js and kotlin/wasm code.
* Given that the Kotlin standard library does not provide common (shared between kotlin/js and kotlin/wasm) W3C API,
* this file serves to fill that void with custom partial declarations.
*
* These declarations do not cover all attributes and methods of the respective W3C types,
* but only those needed by our implementations.
*/
@Suppress("ClassName")
internal external object window {
val devicePixelRatio: Double
val navigator: Navigator
val performance: Performance
fun requestAnimationFrame(block: (Double) -> Unit)
fun open(url: String?, target: String?)
}
internal external interface Performance {
fun now(): Double
}
internal external interface Navigator {
val language: String
val clipboard: Clipboard
val userAgent: String
}
internal external interface Clipboard {
fun writeText(text: String)
}
internal abstract external class HTMLCanvasElement {
internal var width: Int
internal var height: Int
internal val style: CSSStyleDeclaration
}
internal abstract external class CSSStyleDeclaration {
var width: String
var height: String
}
\ No newline at end of file
package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer
import org.w3c.dom.HTMLCanvasElement
import kotlin.js.*
import org.jetbrains.skiko.w3c.HTMLCanvasElement
internal external interface ContextAttributes {
val alpha: Int?
......
package org.jetbrains.skiko
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.HTMLElement
import org.w3c.dom.events.UIEvent
internal actual fun getEventTimestamp(e: UIEvent): Long {
internal fun getEventTimestamp(e: UIEvent): Long {
// TODO: why can't do timestamp.toLong()
return e.timeStamp.toInt().toLong()
}
\ No newline at end of file
}
internal actual fun CursorManager_setCursor(component: Any, cursor: Cursor) {
(component as? HTMLElement)?.style?.cursor = cursor
}
internal actual fun CursorManager_getCursor(component: Any): Cursor? {
return (component as? HTMLElement)?.style?.cursor
}
internal actual fun getNavigatorInfo(): String =
js("navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform")
\ No newline at end of file
package org.jetbrains.skiko
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.wasm.GL
internal actual fun makeGLContextCurrent(contextPointer: NativePointer) {
GL.makeContextCurrent(contextPointer)
}
\ No newline at end of file
package org.jetbrains.skiko
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.events.WheelEvent
internal fun toSkikoEvent(
event: MouseEvent,
kind: SkikoPointerEventKind
): SkikoPointerEvent {
return SkikoPointerEvent(
x = event.offsetX,
y = event.offsetY,
pressedButtons = toSkikoPressedMouseButtons(event, kind),
button = toSkikoMouseButton(event),
modifiers = toSkikoModifiers(event),
kind = kind,
timestamp = getEventTimestamp(event),
platform = event
)
}
internal fun toSkikoDragEvent(
event: MouseEvent
): SkikoPointerEvent {
return SkikoPointerEvent(
x = event.offsetX,
y = event.offsetY,
pressedButtons = SkikoMouseButtons(buttonsFlags),
button = toSkikoMouseButton(event),
modifiers = toSkikoModifiers(event),
kind = SkikoPointerEventKind.DRAG,
timestamp = getEventTimestamp(event),
platform = event
)
}
internal fun toSkikoTypeEvent(
character: String,
event: KeyboardEvent?,
): SkikoInputEvent? {
return if (SPECIAL_KEYS.contains(character)) {
null
} else {
val input = when (character) {
"Enter" -> "\n"
"Tab" -> "\t"
else -> character
}
val key = if (event != null) SkikoKey.valueOf(event.keyCode) else SkikoKey.KEY_UNKNOWN
val modifiers = if (event != null) toSkikoModifiers(event) else SkikoInputModifiers.EMPTY
SkikoInputEvent(
input,
key,
modifiers,
SkikoKeyboardEventKind.TYPE,
event
)
}
}
internal fun toSkikoEvent(
event: KeyboardEvent,
kind: SkikoKeyboardEventKind
): SkikoKeyboardEvent {
return SkikoKeyboardEvent(
SkikoKey.valueOf(toSkikoKey(event)),
toSkikoModifiers(event),
kind,
getEventTimestamp(event),
event
)
}
internal fun toSkikoScrollEvent(
event: WheelEvent,
): SkikoPointerEvent {
return SkikoPointerEvent(
x = event.offsetX,
y = event.offsetY,
deltaX = event.deltaX,
deltaY = event.deltaY,
pressedButtons = SkikoMouseButtons(buttonsFlags),
button = SkikoMouseButtons.NONE,
modifiers = toSkikoModifiers(event),
kind = SkikoPointerEventKind.SCROLL,
timestamp = getEventTimestamp(event),
platform = event
)
}
private var buttonsFlags = 0
private fun toSkikoPressedMouseButtons(
event: MouseEvent,
kind: SkikoPointerEventKind
): SkikoMouseButtons {
// https://www.w3schools.com/jsref/event_button.asp
val button = event.button.toInt()
if (kind == SkikoPointerEventKind.DOWN) {
buttonsFlags = buttonsFlags.or(getSkikoButtonValue(button))
return SkikoMouseButtons(buttonsFlags)
}
buttonsFlags = buttonsFlags.xor(getSkikoButtonValue(button))
return SkikoMouseButtons(buttonsFlags)
}
private fun toSkikoMouseButton(event: MouseEvent): SkikoMouseButtons {
return SkikoMouseButtons(getSkikoButtonValue(event.button.toInt()))
}
private fun getSkikoButtonValue(button: Int): Int {
return when (button) {
0 -> SkikoMouseButtons.LEFT.value
1 -> SkikoMouseButtons.MIDDLE.value
2 -> SkikoMouseButtons.RIGHT.value
3 -> SkikoMouseButtons.BUTTON_4.value
4 -> SkikoMouseButtons.BUTTON_5.value
else -> 0
}
}
private fun toSkikoModifiers(event: MouseEvent): SkikoInputModifiers {
var result = 0
if (event.altKey) {
result = result.or(SkikoInputModifiers.ALT.value)
}
if (event.shiftKey) {
result = result.or(SkikoInputModifiers.SHIFT.value)
}
if (event.ctrlKey) {
result = result.or(SkikoInputModifiers.CONTROL.value)
}
if (event.metaKey) {
result = result.or(SkikoInputModifiers.META.value)
}
return SkikoInputModifiers(result)
}
private fun toSkikoModifiers(event: KeyboardEvent): SkikoInputModifiers {
var result = 0
if (event.altKey) {
result = result.or(SkikoInputModifiers.ALT.value)
}
if (event.shiftKey) {
result = result.or(SkikoInputModifiers.SHIFT.value)
}
if (event.ctrlKey) {
result = result.or(SkikoInputModifiers.CONTROL.value)
}
if (event.metaKey) {
result = result.or(SkikoInputModifiers.META.value)
}
return SkikoInputModifiers(result)
}
private fun toSkikoKey(event: KeyboardEvent): Int {
var key = event.keyCode
val side = event.location
if (side == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
if (
key == SkikoKey.KEY_LEFT_CONTROL.platformKeyCode ||
key == SkikoKey.KEY_LEFT_SHIFT.platformKeyCode ||
key == SkikoKey.KEY_LEFT_META.platformKeyCode
)
key = key.or(0x80000000.toInt())
}
return key
}
\ No newline at end of file
package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import org.w3c.dom.events.KeyboardEvent
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.events.WheelEvent
internal actual fun SkiaLayer.bindCanvasEventsToSkikoView(
canvas: HTMLCanvasElement
) {
val htmlCanvas = canvas as org.w3c.dom.HTMLCanvasElement
htmlCanvas.addEventListener("mousedown", { event ->
event as MouseEvent
isPointerPressed = true
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.DOWN))
})
htmlCanvas.addEventListener("mouseup", { event ->
event as MouseEvent
isPointerPressed = false
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.UP))
})
htmlCanvas.addEventListener("mousemove", { event ->
event as MouseEvent
if (isPointerPressed) {
skikoView?.onPointerEvent(toSkikoDragEvent(event))
} else {
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.MOVE))
}
})
htmlCanvas.addEventListener("wheel", { event ->
event as WheelEvent
skikoView?.onPointerEvent(toSkikoScrollEvent(event))
})
htmlCanvas.addEventListener("contextmenu", { event ->
event.preventDefault()
})
htmlCanvas.addEventListener("keydown", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN))
toSkikoTypeEvent(event.key, event)?.let { inputEvent ->
skikoView?.input?.onInputEvent(inputEvent)
}
})
htmlCanvas.addEventListener("keyup", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP))
})
}
actual typealias SkikoGesturePlatformEvent = Any
actual typealias SkikoPlatformInputEvent = KeyboardEvent
actual typealias SkikoPlatformKeyboardEvent = KeyboardEvent
// MouseEvent is base class of PointerEvent
actual typealias SkikoPlatformPointerEvent = MouseEvent
internal actual fun SkiaLayer.setOnChangeScaleNotifier() {
state?.initCanvas(desiredWidth, desiredHeight, contentScale, this.pixelGeometry)
window.matchMedia("(resolution: ${contentScale}dppx)")
.addEventListener("change", { setOnChangeScaleNotifier() }, true)
onContentScaleChanged?.invoke(contentScale)
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer
import org.w3c.dom.HTMLCanvasElement
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import kotlin.js.*
internal external object GL : GLInterface {
......@@ -11,6 +11,6 @@ internal external object GL : GLInterface {
}
private external interface GLInterface {
fun createContext(context: HTMLCanvasElement, contextAttributes: ContextAttributes): NativePointer;
fun createContext(context: HTMLCanvasElement, contextAttributes: ContextAttributes): NativePointer
fun makeContextCurrent(contextPointer: NativePointer): Boolean;
}
\ No newline at end of file
package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer
import org.w3c.dom.HTMLCanvasElement
import org.jetbrains.skiko.w3c.HTMLCanvasElement
@JsFun(
"""() => {
......
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