Unverified Commit 62ea4669 authored by dima.avdeev's avatar dima.avdeev Committed by GitHub

SkikoKey, expect val platformKeyCode:Int. And uikit backspace key event (#557)

Also fix keyEvent leaks on iOS (#560)
parent c64f55c3
......@@ -2,12 +2,6 @@
package org.jetbrains.skiko.sample
import kotlinx.cinterop.*
import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkikoView
import org.jetbrains.skiko.SkikoViewController
import org.jetbrains.skiko.SkikoUIView
import org.jetbrains.skiko.SkikoGestureEventKind
import platform.UIKit.*
import platform.Foundation.*
......
......@@ -3,7 +3,7 @@ package org.jetbrains.skiko
import android.view.KeyEvent.*
// See https://developer.android.com/reference/android/view/KeyEvent
actual enum class SkikoKey(val value: Int) {
actual enum class SkikoKey(actual val platformKeyCode: Int) {
KEY_UNKNOWN(KEYCODE_UNKNOWN),
KEY_A(KEYCODE_A),
KEY_S(KEYCODE_S),
......@@ -111,8 +111,8 @@ actual enum class SkikoKey(val value: Int) {
KEY_NUMPAD_DECIMAL(KEYCODE_UNKNOWN);
companion object {
fun valueOf(value: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.value == value }
fun valueOf(platformKeyCode: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.platformKeyCode == platformKeyCode }
return key ?: SkikoKey.KEY_UNKNOWN
}
}
......
......@@ -230,14 +230,14 @@ private fun toSkikoKey(event: KeyEvent): Int {
val side = event.getKeyLocation()
if (side == KEY_LOCATION_RIGHT) {
if (
key == SkikoKey.KEY_LEFT_CONTROL.value ||
key == SkikoKey.KEY_LEFT_SHIFT.value ||
key == SkikoKey.KEY_LEFT_META.value
key == SkikoKey.KEY_LEFT_CONTROL.platformKeyCode ||
key == SkikoKey.KEY_LEFT_SHIFT.platformKeyCode ||
key == SkikoKey.KEY_LEFT_META.platformKeyCode
)
key = key.or(0x80000000.toInt())
}
if (side == KEY_LOCATION_NUMPAD) {
if (key == SkikoKey.KEY_ENTER.value) {
if (key == SkikoKey.KEY_ENTER.platformKeyCode) {
key = key.or(0x80000000.toInt())
}
}
......
......@@ -2,7 +2,7 @@ package org.jetbrains.skiko
import java.awt.event.KeyEvent.*
actual enum class SkikoKey(val value: Int) {
actual enum class SkikoKey(actual val platformKeyCode: Int) {
KEY_UNKNOWN(-1),
KEY_A(VK_A),
KEY_S(VK_S),
......@@ -110,8 +110,8 @@ actual enum class SkikoKey(val value: Int) {
KEY_NUMPAD_DECIMAL(VK_DECIMAL);
companion object {
fun valueOf(value: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.value == value }
fun valueOf(platformKeyCode: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.platformKeyCode == platformKeyCode }
return if (key == null) SkikoKey.KEY_UNKNOWN else key
}
}
......
......@@ -94,114 +94,6 @@ value class SkikoInputModifiers(val value: Int) {
}
}
expect enum class SkikoKey {
KEY_UNKNOWN,
KEY_A,
KEY_S,
KEY_D,
KEY_F,
KEY_H,
KEY_G,
KEY_Z,
KEY_X,
KEY_C,
KEY_V,
KEY_B,
KEY_Q,
KEY_W,
KEY_E,
KEY_R,
KEY_Y,
KEY_T,
KEY_U,
KEY_I,
KEY_P,
KEY_L,
KEY_J,
KEY_K,
KEY_N,
KEY_M,
KEY_O,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_0,
KEY_CLOSE_BRACKET,
KEY_OPEN_BRACKET,
KEY_QUOTE,
KEY_SEMICOLON,
KEY_SLASH,
KEY_COMMA,
KEY_BACKSLASH,
KEY_PERIOD,
KEY_BACK_QUOTE,
KEY_EQUALS,
KEY_MINUS,
KEY_ENTER,
KEY_ESCAPE,
KEY_TAB,
KEY_BACKSPACE,
KEY_SPACE,
KEY_CAPSLOCK,
KEY_LEFT_META,
KEY_LEFT_SHIFT,
KEY_LEFT_ALT,
KEY_LEFT_CONTROL,
KEY_RIGHT_META,
KEY_RIGHT_SHIFT,
KEY_RIGHT_ALT,
KEY_RIGHT_CONTROL,
KEY_MENU,
KEY_UP,
KEY_DOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_F11,
KEY_F12,
KEY_PRINTSCEEN,
KEY_SCROLL_LOCK,
KEY_PAUSE,
KEY_INSERT,
KEY_HOME,
KEY_PGUP,
KEY_DELETE,
KEY_END,
KEY_PGDOWN,
KEY_NUM_LOCK,
KEY_NUMPAD_0,
KEY_NUMPAD_1,
KEY_NUMPAD_2,
KEY_NUMPAD_3,
KEY_NUMPAD_4,
KEY_NUMPAD_5,
KEY_NUMPAD_6,
KEY_NUMPAD_7,
KEY_NUMPAD_8,
KEY_NUMPAD_9,
KEY_NUMPAD_ENTER,
KEY_NUMPAD_ADD,
KEY_NUMPAD_SUBTRACT,
KEY_NUMPAD_MULTIPLY,
KEY_NUMPAD_DIVIDE,
KEY_NUMPAD_DECIMAL;
}
enum class SkikoGestureEventKind {
TAP, DOUBLETAP, LONGPRESS, PAN, PINCH, ROTATION, SWIPE, UNKNOWN
}
......
package org.jetbrains.skiko
expect enum class SkikoKey {
KEY_UNKNOWN,
KEY_A,
KEY_S,
KEY_D,
KEY_F,
KEY_H,
KEY_G,
KEY_Z,
KEY_X,
KEY_C,
KEY_V,
KEY_B,
KEY_Q,
KEY_W,
KEY_E,
KEY_R,
KEY_Y,
KEY_T,
KEY_U,
KEY_I,
KEY_P,
KEY_L,
KEY_J,
KEY_K,
KEY_N,
KEY_M,
KEY_O,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_0,
KEY_CLOSE_BRACKET,
KEY_OPEN_BRACKET,
KEY_QUOTE,
KEY_SEMICOLON,
KEY_SLASH,
KEY_COMMA,
KEY_BACKSLASH,
KEY_PERIOD,
KEY_BACK_QUOTE,
KEY_EQUALS,
KEY_MINUS,
KEY_ENTER,
KEY_ESCAPE,
KEY_TAB,
KEY_BACKSPACE,
KEY_SPACE,
KEY_CAPSLOCK,
KEY_LEFT_META,
KEY_LEFT_SHIFT,
KEY_LEFT_ALT,
KEY_LEFT_CONTROL,
KEY_RIGHT_META,
KEY_RIGHT_SHIFT,
KEY_RIGHT_ALT,
KEY_RIGHT_CONTROL,
KEY_MENU,
KEY_UP,
KEY_DOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_F11,
KEY_F12,
KEY_PRINTSCEEN,
KEY_SCROLL_LOCK,
KEY_PAUSE,
KEY_INSERT,
KEY_HOME,
KEY_PGUP,
KEY_DELETE,
KEY_END,
KEY_PGDOWN,
KEY_NUM_LOCK,
KEY_NUMPAD_0,
KEY_NUMPAD_1,
KEY_NUMPAD_2,
KEY_NUMPAD_3,
KEY_NUMPAD_4,
KEY_NUMPAD_5,
KEY_NUMPAD_6,
KEY_NUMPAD_7,
KEY_NUMPAD_8,
KEY_NUMPAD_9,
KEY_NUMPAD_ENTER,
KEY_NUMPAD_ADD,
KEY_NUMPAD_SUBTRACT,
KEY_NUMPAD_MULTIPLY,
KEY_NUMPAD_DIVIDE,
KEY_NUMPAD_DECIMAL;
val platformKeyCode: Int
}
......@@ -109,8 +109,19 @@ actual enum class SkikoKey(val value: UIKeyboardHIDUsage) {
KEY_NUMPAD_DIVIDE(UIKeyboardHIDUsageKeypadSlash),
KEY_NUMPAD_DECIMAL(99);
init {
if (value > Int.MAX_VALUE) {
error("iOS SkikoKey, init value = $value > Int.MAX_VALUE")
}
}
actual val platformKeyCode get() = value.toInt()
companion object {
fun valueOf(value: UIKeyboardHIDUsage): SkikoKey {
if (value > Int.MAX_VALUE) {
error("iOS SkikoKey, valueOf value = $value > Int.MAX_VALUE")
}
val key = SkikoKey.values().firstOrNull { it.value == value }
return key ?: SkikoKey.KEY_UNKNOWN
}
......
package org.jetbrains.skiko
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExportObjCClass
import kotlinx.cinterop.useContents
import platform.CoreGraphics.CGRect
import platform.CoreGraphics.CGRectMake
import platform.Foundation.NSCoder
import platform.UIKit.UIEvent
import platform.UIKit.UITouch
import platform.UIKit.UIScreen
import platform.UIKit.UIView
import platform.UIKit.setFrame
import platform.UIKit.contentScaleFactor
import platform.UIKit.UIKeyInputProtocol
import platform.UIKit.UIPress
import platform.UIKit.UIPressesEvent
@ExportObjCClass
class SkikoUIView : UIView, UIKeyInputProtocol {
@OverrideInit
constructor(frame: CValue<CGRect>) : super(frame)
@OverrideInit
constructor(coder: NSCoder) : super(coder)
private var skiaLayer: SkiaLayer? = null
constructor(skiaLayer: SkiaLayer, frame: CValue<CGRect> = CGRectMake(0.0, 0.0, 1.0, 1.0)) : super(frame) {
this.skiaLayer = skiaLayer
}
fun detach() = skiaLayer?.detach()
fun load(): SkikoUIView {
val (width, height) = UIScreen.mainScreen.bounds.useContents {
this.size.width to this.size.height
}
setFrame(CGRectMake(0.0, 0.0, width, height))
contentScaleFactor = UIScreen.mainScreen.scale
skiaLayer?.let { layer ->
layer.attachTo(this)
layer.initGestures()
}
return this
}
fun showScreenKeyboard() = becomeFirstResponder()
fun hideScreenKeyboard() = resignFirstResponder()
fun isScreenKeyboardOpen() = isFirstResponder
private val pressedKeycodes: MutableSet<Long> = mutableSetOf()
@Deprecated("need be deleted")
private var inputText: String = ""//todo delete, because it's redundant and may leaks memory
override fun hasText(): Boolean {
return inputText.length > 0
}
override fun insertText(theText: String) {
inputText += theText
skiaLayer?.skikoView?.onInputEvent(toSkikoTypeEvent(theText, null))
}
override fun deleteBackward() {
inputText = inputText.dropLast(1)
if (!pressedKeycodes.contains(SkikoKey.KEY_BACKSPACE.value)) {
val downEvent = SkikoKeyboardEvent(
key = SkikoKey.KEY_BACKSPACE,
kind = SkikoKeyboardEventKind.DOWN,
platform = null
)
val upEvent = downEvent.copy(
kind = SkikoKeyboardEventKind.UP
)
skiaLayer?.skikoView?.onKeyboardEvent(downEvent)
skiaLayer?.skikoView?.onKeyboardEvent(upEvent)
}
}
override fun canBecomeFirstResponder() = true
override fun pressesBegan(presses: Set<*>, withEvent: UIPressesEvent?) {
if (withEvent != null) {
for (press in withEvent.allPresses) {
val uiPress = press as? UIPress
if (uiPress != null) {
uiPress.key?.let {
pressedKeycodes.add(it.keyCode)
}
skiaLayer?.skikoView?.onKeyboardEvent(
toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.DOWN)
)
}
}
}
super.pressesBegan(presses, withEvent)
}
override fun pressesEnded(presses: Set<*>, withEvent: UIPressesEvent?) {
if (withEvent != null) {
for (press in withEvent.allPresses) {
val uiPress = press as? UIPress
if (uiPress != null) {
uiPress.key?.let {
pressedKeycodes.remove(it.keyCode)
}
skiaLayer?.skikoView?.onKeyboardEvent(
toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.UP)
)
}
}
}
super.pressesEnded(presses, withEvent)
}
override fun touchesBegan(touches: Set<*>, withEvent: UIEvent?) {
super.touchesBegan(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.STARTED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
override fun touchesEnded(touches: Set<*>, withEvent: UIEvent?) {
super.touchesEnded(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.ENDED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
override fun touchesMoved(touches: Set<*>, withEvent: UIEvent?) {
super.touchesMoved(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.MOVED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
override fun touchesCancelled(touches: Set<*>, withEvent: UIEvent?) {
super.touchesCancelled(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.CANCELLED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
}
package org.jetbrains.skiko
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExportObjCClass
import kotlinx.cinterop.useContents
import platform.CoreGraphics.CGRect
import platform.CoreGraphics.CGRectMake
import platform.Foundation.NSCoder
import platform.UIKit.UIEvent
import platform.UIKit.UITouch
import platform.UIKit.UIScreen
import platform.UIKit.UIView
import platform.UIKit.UIViewController
import platform.UIKit.setFrame
import platform.UIKit.contentScaleFactor
import platform.UIKit.UIKeyInputProtocol
import platform.UIKit.UIPress
import platform.UIKit.UIPressesEvent
import kotlinx.cinterop.*
import platform.Foundation.*
import platform.UIKit.*
@ExportObjCClass
class SkikoViewController : UIViewController {
......@@ -38,139 +25,13 @@ class SkikoViewController : UIViewController {
}
}
override fun viewDidLoad() {
super.viewDidLoad()
skikoUIView?.showScreenKeyboard()
}
// viewDidUnload() is deprecated and not called.
override fun viewDidDisappear(animated: Boolean) {
skikoUIView?.detach()
}
}
@ExportObjCClass
class SkikoUIView : UIView, UIKeyInputProtocol {
@OverrideInit
constructor(frame: CValue<CGRect>) : super(frame)
@OverrideInit
constructor(coder: NSCoder) : super(coder)
private var skiaLayer: SkiaLayer? = null
constructor(skiaLayer: SkiaLayer, frame: CValue<CGRect> = CGRectMake(0.0, 0.0, 1.0, 1.0)) : super(frame) {
this.skiaLayer = skiaLayer
}
fun detach() = skiaLayer?.detach()
fun load(): SkikoUIView {
val (width, height) = UIScreen.mainScreen.bounds.useContents {
this.size.width to this.size.height
}
setFrame(CGRectMake(0.0, 0.0, width, height))
contentScaleFactor = UIScreen.mainScreen.scale
skiaLayer?.let { layer ->
layer.attachTo(this)
layer.initGestures()
}
return this
}
fun showScreenKeyboard() = becomeFirstResponder()
fun hideScreenKeyboard() = resignFirstResponder()
fun isScreenKeyboardOpen() = isFirstResponder
var keyEvent: UIPress? = null
private var inputText: String = ""
override fun hasText(): Boolean {
return inputText.length > 0
}
override fun insertText(theText: String) {
inputText += theText
skiaLayer?.skikoView?.onInputEvent(toSkikoTypeEvent(theText, keyEvent))
}
override fun deleteBackward() {
inputText = inputText.dropLast(1)
}
override fun canBecomeFirstResponder() = true
override fun pressesBegan(presses: Set<*>, withEvent: UIPressesEvent?) {
if (withEvent != null) {
for (press in withEvent.allPresses) {
keyEvent = press as UIPress
skiaLayer?.skikoView?.onKeyboardEvent(
toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.DOWN)
)
}
}
super.pressesBegan(presses, withEvent)
}
override fun pressesEnded(presses: Set<*>, withEvent: UIPressesEvent?) {
if (withEvent != null) {
for (press in withEvent.allPresses) {
keyEvent = press as UIPress
skiaLayer?.skikoView?.onKeyboardEvent(
toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.UP)
)
}
}
super.pressesEnded(presses, withEvent)
}
override fun touchesBegan(touches: Set<*>, withEvent: UIEvent?) {
super.touchesBegan(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.STARTED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
override fun touchesEnded(touches: Set<*>, withEvent: UIEvent?) {
super.touchesEnded(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.ENDED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
override fun touchesMoved(touches: Set<*>, withEvent: UIEvent?) {
super.touchesMoved(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.MOVED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
override fun touchesCancelled(touches: Set<*>, withEvent: UIEvent?) {
super.touchesCancelled(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf()
for (touch in touches) {
val event = touch as UITouch
val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong()
events.add(
SkikoTouchEvent(x, y, SkikoTouchEventKind.CANCELLED, timestamp, event)
)
}
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
}
}
......@@ -214,9 +214,9 @@ private fun toSkikoKey(event: KeyboardEvent): Int {
val side = event.location
if (side == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
if (
key == SkikoKey.KEY_LEFT_CONTROL.value ||
key == SkikoKey.KEY_LEFT_SHIFT.value ||
key == SkikoKey.KEY_LEFT_META.value
key == SkikoKey.KEY_LEFT_CONTROL.platformKeyCode ||
key == SkikoKey.KEY_LEFT_SHIFT.platformKeyCode ||
key == SkikoKey.KEY_LEFT_META.platformKeyCode
)
key = key.or(0x80000000.toInt())
}
......
package org.jetbrains.skiko
actual enum class SkikoKey(val value: Int) {
actual enum class SkikoKey(actual val platformKeyCode: Int) {
KEY_UNKNOWN(-1),
KEY_A(65),
KEY_S(83),
......@@ -108,8 +108,8 @@ actual enum class SkikoKey(val value: Int) {
KEY_NUMPAD_DECIMAL(110);
companion object {
fun valueOf(value: Int): SkikoKey {
return values().firstOrNull { it.value == value } ?: KEY_UNKNOWN
fun valueOf(platformKeyCode: Int): SkikoKey {
return values().firstOrNull { it.platformKeyCode == platformKeyCode } ?: KEY_UNKNOWN
}
}
}
package org.jetbrains.skiko
actual enum class SkikoKey(val value: Int) {
actual enum class SkikoKey(actual val platformKeyCode: Int) {
KEY_UNKNOWN(-1),
KEY_A(0),
KEY_S(1),
......@@ -108,8 +108,8 @@ actual enum class SkikoKey(val value: Int) {
KEY_NUMPAD_DECIMAL(65);
companion object {
fun valueOf(value: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.value == value }
fun valueOf(platformKeyCode: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.platformKeyCode == platformKeyCode }
return if (key == null) SkikoKey.KEY_UNKNOWN else key
}
}
......
package org.jetbrains.skiko
actual enum class SkikoKey(val value: Int) {
actual enum class SkikoKey(actual val platformKeyCode: Int) {
KEY_UNKNOWN(-1),
KEY_A(0),
KEY_S(1),
......@@ -108,8 +108,8 @@ actual enum class SkikoKey(val value: Int) {
KEY_NUMPAD_DECIMAL(65);
companion object {
fun valueOf(value: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.value == value }
fun valueOf(platformKeyCode: Int): SkikoKey {
val key = SkikoKey.values().firstOrNull { it.platformKeyCode == platformKeyCode }
return if (key == null) SkikoKey.KEY_UNKNOWN else key
}
}
......
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