Unverified Commit a171fead authored by Roman Sedaikin's avatar Roman Sedaikin Committed by GitHub

Skiko events update. (#386)

parent db8a94cc
......@@ -76,6 +76,11 @@ tasks.register("runWithTransparency") {
dependsOn(casualRun)
}
tasks.register("runInterop") {
additionalArguments += mapOf("skiko.swing.interop" to "true")
dependsOn(casualRun)
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
......@@ -11,10 +11,13 @@ import java.io.File
import java.nio.file.Files
import javax.imageio.ImageIO
fun main() {
val windows = 1
fun main(args: Array<String>) {
val windows = parseArgs(args)
repeat(windows) {
createWindow("window $it", windows == 1)
when (System.getProperty("skiko.interop")) {
"true" -> SwingSkia()
else -> createWindow("window $it", windows == 1)
}
}
}
......@@ -91,14 +94,7 @@ fun createWindow(title: String, exitOnClose: Boolean) = SwingUtilities.invokeLat
println("Changed renderer for $layer: new value is ${layer.renderApi}")
}
skiaLayer.skikoView = GenericSkikoView(skiaLayer, clocks)
skiaLayer.addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(event: MouseEvent) {
clocks.xpos = event.x
clocks.ypos = event.y
}
})
skiaLayer.addView(GenericSkikoView(skiaLayer, clocks))
// Window transparency
if (System.getProperty("skiko.transparency") == "true") {
......@@ -120,3 +116,17 @@ fun createWindow(title: String, exitOnClose: Boolean) = SwingUtilities.invokeLat
skiaLayer.paint(window.graphics)
window.isVisible = true
}
private fun parseArgs(args: Array<String>): Int {
var windows = 1
for(arg in args) {
try {
windows = arg.toInt()
break
}
catch(e: NumberFormatException) {
println("The passed argument:($arg) is not a integer number!")
}
}
return windows
}
\ No newline at end of file
......@@ -20,8 +20,8 @@ class ClocksJvm(private val layer: SkiaLayer): SkikoView {
}
private var frame = 0
var xpos = 0
var ypos = 0
var xpos = 0.0
var ypos = 0.0
private val fontCollection = FontCollection()
.setDefaultFontManager(FontMgr.default)
......@@ -100,4 +100,23 @@ class ClocksJvm(private val layer: SkiaLayer): SkikoView {
canvas.drawPicture(picture, null, Paint())
canvas.drawLine(left, top + rectH, left + rectW, top, Paint())
}
override fun onPointerEvent(event: SkikoPointerEvent) {
when (event.kind) {
SkikoPointerEventKind.DOWN,
SkikoPointerEventKind.MOVE -> {
xpos = event.x
ypos = event.y
}
}
// TODO: provide example that covers all features of pointer event
}
override fun onInputEvent(event: SkikoInputEvent) {
// TODO: provide example that covers all features of text input event
}
override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
// TODO: provide example that covers all features of keyboard event
}
}
\ No newline at end of file
......@@ -81,30 +81,23 @@ fun SwingSkia() = SwingUtilities.invokeLater {
}
private fun getSkiaPanel(): SkiaPanel {
val panel = SkiaPanel()
val clocks = ClocksJvm(panel.layer)
panel.layer.skikoView = GenericSkikoView(panel.layer, clocks)
val btnPanelOK = JPanel()
btnPanelOK.setLayout(BorderLayout(0, 0))
btnPanelOK.setBackground(Color.white)
btnPanelOK.add(JButton("OK"))
val btnCancel = JButton("Cancel")
panel.add(btnPanelOK)
panel.add(btnCancel)
panel.addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
btnPanelOK.setBounds(panel.width - 200, panel.height - 100, 200, 40)
btnCancel.setBounds(panel.width - 200, panel.height - 50, 200, 40)
panel.invalidate()
panel.validate()
panel.repaint()
}
})
panel.layer.addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(event: MouseEvent) {
clocks.xpos = event.x
clocks.ypos = event.y
}
})
return panel
return SkiaPanel().apply {
layer.addView(GenericSkikoView(layer, ClocksJvm(layer)))
val btnPanelOK = JPanel()
btnPanelOK.setLayout(BorderLayout(0, 0))
btnPanelOK.setBackground(Color.white)
btnPanelOK.add(JButton("OK"))
val btnCancel = JButton("Cancel")
add(btnPanelOK)
add(btnCancel)
addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
btnPanelOK.setBounds(width - 200, height - 100, 200, 40)
btnCancel.setBounds(width - 200, height - 50, 200, 40)
invalidate()
validate()
repaint()
}
})
}
}
......@@ -5,14 +5,7 @@ import org.jetbrains.skia.paragraph.FontCollection
import org.jetbrains.skia.paragraph.ParagraphBuilder
import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.TextStyle
import org.jetbrains.skiko.currentSystemTheme
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkikoView
import org.jetbrains.skiko.SkikoPointerEvent
import org.jetbrains.skiko.SkikoGestureEvent
import org.jetbrains.skiko.SkikoGestureEventKind
import org.jetbrains.skiko.SkikoGestureEventState
import org.jetbrains.skiko.isLeftClick
import org.jetbrains.skiko.*
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.PI
......@@ -103,10 +96,24 @@ class Clocks(private val layer: SkiaLayer): SkikoView {
}
override fun onPointerEvent(event: SkikoPointerEvent) {
if (event.isLeftClick) {
xpos = event.x
ypos = event.y
when (event.kind) {
SkikoPointerEventKind.DOWN,
SkikoPointerEventKind.MOVE -> {
xpos = event.x
ypos = event.y
}
else -> {}
}
// TODO: provide example that covers all features of pointer event
}
override fun onInputEvent(event: SkikoInputEvent) {
// TODO: provide example that covers all features of text input event
}
override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
println(event.kind)
// TODO: provide example that covers all features of keyboard event
}
override fun onGestureEvent(event: SkikoGestureEvent) {
......
package org.jetbrains.skiko
// Clicked mouse buttons bitmask.
object SkikoMouseButtons {
const val NONE = 0
const val LEFT = 1 shl 0
const val RIGHT = 1 shl 1
const val MIDDLE = 1 shl 2
inline class SkikoMouseButtons(val value: Int) {
companion object {
val NONE = SkikoMouseButtons(0)
val LEFT = SkikoMouseButtons(1)
val RIGHT = SkikoMouseButtons(2)
val MIDDLE = SkikoMouseButtons(4)
val BUTTON_1 = SkikoMouseButtons(1)
val BUTTON_2 = SkikoMouseButtons(2)
val BUTTON_3 = SkikoMouseButtons(4)
val BUTTON_4 = SkikoMouseButtons(8)
val BUTTON_5 = SkikoMouseButtons(16)
val BUTTON_6 = SkikoMouseButtons(32)
val BUTTON_7 = SkikoMouseButtons(64)
val BUTTON_8 = SkikoMouseButtons(128)
}
fun has(value: SkikoMouseButtons): Boolean {
if (value.value and this.value != 0) {
return true
}
return false
}
override fun toString(): String {
val result = mutableListOf<String>().apply {
if (has(SkikoMouseButtons.LEFT)) {
add("LEFT")
}
if (has(SkikoMouseButtons.RIGHT)) {
add("RIGHT")
}
if (has(SkikoMouseButtons.MIDDLE)) {
add("MIDDLE")
}
if (has(SkikoMouseButtons.BUTTON_4)) {
add("BUTTON_4")
}
if (has(SkikoMouseButtons.BUTTON_5)) {
add("BUTTON_5")
}
if (has(SkikoMouseButtons.BUTTON_6)) {
add("BUTTON_6")
}
if (has(SkikoMouseButtons.BUTTON_7)) {
add("BUTTON_7")
}
if (has(SkikoMouseButtons.BUTTON_8)) {
add("BUTTON_8")
}
}
return if (!result.isEmpty()) result.toString() else ""
}
}
inline class SkikoInputModifiers(val value: Int) {
companion object {
val EMPTY = SkikoInputModifiers(0)
val META = SkikoInputModifiers(1)
val CONTROL = SkikoInputModifiers(2)
val ALT = SkikoInputModifiers(4)
val SHIFT = SkikoInputModifiers(8)
}
fun has(value: SkikoInputModifiers): Boolean {
if (value.value and this.value != 0) {
return true
}
return false
}
override fun toString(): String {
val result = mutableListOf<String>().apply {
if (has(SkikoInputModifiers.META)) {
add("META")
}
if (has(SkikoInputModifiers.CONTROL)) {
add("CONTROL")
}
if (has(SkikoInputModifiers.ALT)) {
add("ALT")
}
if (has(SkikoInputModifiers.SHIFT)) {
add("SHIFT")
}
}
return if (!result.isEmpty()) result.toString() else ""
}
}
enum class SkikoGestureEventKind {
......@@ -22,7 +104,6 @@ data class SkikoGestureEvent(
val x: Double, val y: Double,
val velocity: Double = 0.0,
val direction: SkikoGestureEventDirection = SkikoGestureEventDirection.UNKNOWN,
val interval: Long = 0,
val rotation: Double = 0.0,
val scale: Double = 1.0,
val kind: SkikoGestureEventKind,
......@@ -42,6 +123,7 @@ enum class SkikoKeyboardEventKind {
expect class SkikoPlatformKeyboardEvent
data class SkikoKeyboardEvent(
val code: Int,
val modifiers: SkikoInputModifiers = SkikoInputModifiers.EMPTY,
val kind: SkikoKeyboardEventKind,
val platform: SkikoPlatformKeyboardEvent?
)
......@@ -52,14 +134,18 @@ enum class SkikoPointerEventKind {
expect class SkikoPlatformPointerEvent
data class SkikoPointerEvent(
val x: Double, val y: Double,
val buttonMask: Int,
val buttons: SkikoMouseButtons = SkikoMouseButtons.NONE,
val modifiers: SkikoInputModifiers = SkikoInputModifiers.EMPTY,
val kind: SkikoPointerEventKind,
val platform: SkikoPlatformPointerEvent?
)
val SkikoPointerEvent.isLeftClick: Boolean
get() = (buttonMask and SkikoMouseButtons.LEFT) != 0 && (kind == SkikoPointerEventKind.UP)
get() = buttons.has(SkikoMouseButtons.LEFT) && (kind == SkikoPointerEventKind.UP)
val SkikoPointerEvent.isRightClick: Boolean
get() = (buttonMask and SkikoMouseButtons.RIGHT) != 0 && (kind == SkikoPointerEventKind.UP)
get() = buttons.has(SkikoMouseButtons.RIGHT) && (kind == SkikoPointerEventKind.UP)
val SkikoPointerEvent.isMiddleClick: Boolean
get() = buttons.has(SkikoMouseButtons.MIDDLE) && (kind == SkikoPointerEventKind.UP)
package org.jetbrains.skiko
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.events.KeyboardEvent
fun toSkikoEvent(
event: MouseEvent,
buttons: Boolean,
kind: SkikoPointerEventKind
): SkikoPointerEvent {
return SkikoPointerEvent(
event.offsetX,
event.offsetY,
toSkikoMouseButtons(event, buttons),
toSkikoModifiers(event),
kind,
event
)
}
fun toSkikoEvent(
event: KeyboardEvent,
kind: SkikoKeyboardEventKind
): SkikoKeyboardEvent {
return SkikoKeyboardEvent(
event.keyCode,
toSkikoModifiers(event),
kind,
event
)
}
private fun toSkikoMouseButtons(event: MouseEvent, pressed: Boolean): SkikoMouseButtons {
// https://www.w3schools.com/jsref/event_button.asp
var result = 0
if (pressed && event.button.toInt() == 0) {
result = result.or(SkikoMouseButtons.LEFT.value)
}
if (pressed && event.button.toInt() == 2) {
result = result.or(SkikoMouseButtons.RIGHT.value)
}
if (pressed && event.button.toInt() == 1) {
result = result.or(SkikoMouseButtons.MIDDLE.value)
}
return SkikoMouseButtons(result)
}
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)
}
\ No newline at end of file
......@@ -29,23 +29,6 @@ actual open class SkiaLayer {
actual var skikoView: SkikoView? = null
fun translatePointerEvent(
event: MouseEvent,
buttons: Boolean,
kind: SkikoPointerEventKind
): SkikoPointerEvent {
var mask = SkikoMouseButtons.NONE
if (buttons && event.button.toInt() == 0)
mask = mask or SkikoMouseButtons.LEFT
// https://www.w3schools.com/jsref/event_button.asp
if (buttons && event.button.toInt() == 2)
mask = mask or SkikoMouseButtons.RIGHT
return SkikoPointerEvent(
event.offsetX, event.offsetY,
mask, kind, event
)
}
actual fun attachTo(container: Any) {
attachTo(container as HTMLCanvasElement, false)
}
......@@ -66,36 +49,26 @@ actual open class SkiaLayer {
// https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
htmlCanvas.addEventListener("pointerdown", { event ->
event as MouseEvent
skikoView?.onPointerEvent(translatePointerEvent(
event, true, SkikoPointerEventKind.DOWN))
skikoView?.onPointerEvent(toSkikoEvent(event, true, SkikoPointerEventKind.DOWN))
})
htmlCanvas.addEventListener("pointerup", { event ->
event as MouseEvent
var mask = SkikoMouseButtons.NONE
skikoView?.onPointerEvent(translatePointerEvent(
event, true, SkikoPointerEventKind.UP))
skikoView?.onPointerEvent(toSkikoEvent(event, true, SkikoPointerEventKind.UP))
})
htmlCanvas.addEventListener("pointermove", { event ->
event as MouseEvent
skikoView?.onPointerEvent(translatePointerEvent(
event, false, SkikoPointerEventKind.MOVE))
skikoView?.onPointerEvent(toSkikoEvent(event, false, SkikoPointerEventKind.MOVE))
})
htmlCanvas.addEventListener("contextmenu", { event ->
event.preventDefault()
})
htmlCanvas.addEventListener("keydown", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(
SkikoKeyboardEvent(
event.keyCode, SkikoKeyboardEventKind.DOWN, event
)
)
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN))
})
htmlCanvas.addEventListener("keyup", { event ->
event as KeyboardEvent
skikoView?.onKeyboardEvent(SkikoKeyboardEvent(
event.keyCode, SkikoKeyboardEventKind.UP, event
))
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP))
})
}
}
......
......@@ -82,12 +82,8 @@ fun toSkikoEvent(e: MouseEvent): SkikoPointerEvent {
return SkikoPointerEvent(
e.x.toDouble(),
e.y.toDouble(),
when(e.button) {
MouseEvent.BUTTON1 -> SkikoMouseButtons.LEFT
MouseEvent.BUTTON2 -> SkikoMouseButtons.RIGHT
MouseEvent.BUTTON3 -> SkikoMouseButtons.MIDDLE
else -> SkikoMouseButtons.NONE
},
toSkikoMouseButtons(e.modifiersEx),
toSkikoModifiers(e.modifiersEx),
when(e.id) {
MouseEvent.MOUSE_PRESSED -> SkikoPointerEventKind.DOWN
MouseEvent.MOUSE_RELEASED -> SkikoPointerEventKind.UP
......@@ -105,7 +101,8 @@ fun toSkikoEvent(e: MouseWheelEvent): SkikoPointerEvent {
return SkikoPointerEvent(
e.x.toDouble(),
e.y.toDouble(),
SkikoMouseButtons.NONE,
toSkikoMouseButtons(e.modifiersEx),
toSkikoModifiers(e.modifiersEx),
when(e.id) {
MouseEvent.MOUSE_WHEEL-> SkikoPointerEventKind.SCROLL
else -> SkikoPointerEventKind.UNKNOWN
......@@ -117,6 +114,7 @@ fun toSkikoEvent(e: MouseWheelEvent): SkikoPointerEvent {
fun toSkikoEvent(e: KeyEvent): SkikoKeyboardEvent {
return SkikoKeyboardEvent(
e.keyCode,
toSkikoModifiers(e.modifiersEx),
when(e.id) {
KeyEvent.KEY_PRESSED -> SkikoKeyboardEventKind.DOWN
KeyEvent.KEY_RELEASED -> SkikoKeyboardEventKind.UP
......@@ -129,7 +127,38 @@ fun toSkikoEvent(e: KeyEvent): SkikoKeyboardEvent {
fun toSkikoEvent(e: InputMethodEvent): SkikoInputEvent {
return SkikoInputEvent(
"",
"", // TODO: this parameter should be reconsidered
e
)
}
private fun toSkikoMouseButtons(buttons: Int): SkikoMouseButtons {
var result = 0
if (buttons and InputEvent.BUTTON1_DOWN_MASK != 0) {
result = result.or(SkikoMouseButtons.LEFT.value)
}
if (buttons and InputEvent.BUTTON2_DOWN_MASK != 0) {
result = result.or(SkikoMouseButtons.RIGHT.value)
}
if (buttons and InputEvent.BUTTON3_DOWN_MASK != 0) {
result = result.or(SkikoMouseButtons.MIDDLE.value)
}
return SkikoMouseButtons(result)
}
private fun toSkikoModifiers(modifiers: Int): SkikoInputModifiers {
var result = 0
if (modifiers and InputEvent.ALT_DOWN_MASK != 0) {
result = result.or(SkikoInputModifiers.ALT.value)
}
if (modifiers and InputEvent.SHIFT_DOWN_MASK != 0) {
result = result.or(SkikoInputModifiers.SHIFT.value)
}
if (modifiers and InputEvent.CTRL_DOWN_MASK != 0) {
result = result.or(SkikoInputModifiers.CONTROL.value)
}
if (modifiers and InputEvent.META_DOWN_MASK != 0) {
result = result.or(SkikoInputModifiers.META.value)
}
return SkikoInputModifiers(result)
}
package org.jetbrains.skiko
import kotlinx.cinterop.useContents
import platform.AppKit.*
fun toSkikoEvent(
event: NSEvent,
kind: SkikoPointerEventKind,
view: NSView
): SkikoPointerEvent {
var (xpos, ypos) = event.locationInWindow.useContents {
x to y
}
// Translate.
view.frame.useContents {
ypos = size.height - ypos
}
return SkikoPointerEvent(
xpos,
ypos,
toSkikoMouseButtons(event),
toSkikoModifiers(event),
kind,
event
)
}
fun toSkikoEvent(
event: NSEvent,
kind: SkikoKeyboardEventKind
): SkikoKeyboardEvent {
return SkikoKeyboardEvent(
event.keyCode.toInt(),
toSkikoModifiers(event),
kind,
event
)
}
private fun toSkikoMouseButtons(event: NSEvent): SkikoMouseButtons {
var result = 0
val mask = event.buttonMask.toInt()
if ((mask and 1) != 0 || event.buttonNumber == 0L) {
result = result.or(SkikoMouseButtons.LEFT.value)
}
if ((mask and 2) != 0 || event.buttonNumber == 1L) {
result = result.or(SkikoMouseButtons.RIGHT.value)
}
return SkikoMouseButtons(result)
}
private fun toSkikoModifiers(event: NSEvent): SkikoInputModifiers {
return SkikoInputModifiers.EMPTY
}
\ No newline at end of file
......@@ -44,28 +44,6 @@ actual open class SkiaLayer() {
private var picture: PictureHolder? = null
private val pictureRecorder = PictureRecorder()
private fun eventToMouse(event: NSEvent, kind: SkikoPointerEventKind): SkikoPointerEvent {
var buttons = SkikoMouseButtons.NONE
val mask = event.buttonMask.toInt()
if ((mask and 1) != 0 || event.buttonNumber == 0L) {
buttons = buttons or SkikoMouseButtons.LEFT
}
if ((mask and 2) != 0 || event.buttonNumber == 1L) {
buttons = buttons or SkikoMouseButtons.RIGHT
}
var (x, y) = event.locationInWindow.useContents {
this.x to this.y
}
// Translate.
nsView.frame.useContents {
y = size.height - y
}
return SkikoPointerEvent(x, y, buttons, kind, event)
}
private fun eventToKeyboard(event: NSEvent, kind: SkikoKeyboardEventKind): SkikoKeyboardEvent {
return SkikoKeyboardEvent(event.keyCode.toInt(), kind, event)
}
actual fun attachTo(container: Any) {
attachTo(container as NSWindow)
}
......@@ -87,31 +65,37 @@ actual open class SkiaLayer() {
override fun updateTrackingAreas() {
trackingArea?.let { removeTrackingArea(it) }
trackingArea = NSTrackingArea(rect = bounds,
options = NSMouseMoved or NSTrackingActiveInActiveApp,
options = NSTrackingActiveAlways or
NSTrackingMouseEnteredAndExited or
NSTrackingMouseMoved or
NSTrackingInVisibleRect,
owner = nsView, userInfo = null)
nsView.addTrackingArea(trackingArea!!)
}
override fun rightMouseDown(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.DOWN))
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.DOWN, nsView))
}
override fun rightMouseUp(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.UP))
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.UP, nsView))
}
override fun mouseDown(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.DOWN))
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.DOWN, nsView))
}
override fun mouseUp(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.UP))
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.UP, nsView))
}
override fun mouseMoved(event: NSEvent) {
skikoView?.onPointerEvent(eventToMouse(event, SkikoPointerEventKind.MOVE))
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.MOVE, nsView))
}
override fun mouseDragged(event: NSEvent) {
skikoView?.onPointerEvent(toSkikoEvent(event, SkikoPointerEventKind.DRAG, nsView))
}
override fun keyDown(event: NSEvent) {
skikoView?.onKeyboardEvent(eventToKeyboard(event, SkikoKeyboardEventKind.DOWN))
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN))
}
override fun keyUp(event: NSEvent) {
skikoView?.onKeyboardEvent(eventToKeyboard(event, SkikoKeyboardEventKind.UP))
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP))
}
@ObjCAction
......
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