Unverified Commit 47c33a3e authored by dima.avdeev's avatar dima.avdeev Committed by GitHub

Text input multistage for China and Japan languages (#583)

parent 52640c31
package org.jetbrains.skiko
actual interface SkikoInput {
actual object Empty : SkikoInput
}
package org.jetbrains.skiko
actual interface SkikoInput {
actual object Empty : SkikoInput
}
package org.jetbrains.skiko
expect interface SkikoInput {
object Empty : SkikoInput
}
...@@ -6,7 +6,10 @@ interface SkikoView { ...@@ -6,7 +6,10 @@ interface SkikoView {
// Input // Input
fun onKeyboardEvent(event: SkikoKeyboardEvent) = Unit fun onKeyboardEvent(event: SkikoKeyboardEvent) = Unit
fun onPointerEvent(event: SkikoPointerEvent) = Unit fun onPointerEvent(event: SkikoPointerEvent) = Unit
@Deprecated("This method will be removed. Use override val input: SkikoInput")
fun onInputEvent(event: SkikoInputEvent) = Unit fun onInputEvent(event: SkikoInputEvent) = Unit
val input: SkikoInput get() = SkikoInput.Empty
fun onTouchEvent(events: Array<SkikoTouchEvent>) = Unit fun onTouchEvent(events: Array<SkikoTouchEvent>) = Unit
fun onGestureEvent(event: SkikoGestureEvent) = Unit fun onGestureEvent(event: SkikoGestureEvent) = Unit
...@@ -31,6 +34,8 @@ open class GenericSkikoView( ...@@ -31,6 +34,8 @@ open class GenericSkikoView(
app.onInputEvent(event) app.onInputEvent(event)
} }
override val input: SkikoInput get() = app.input
override fun onKeyboardEvent(event: SkikoKeyboardEvent) { override fun onKeyboardEvent(event: SkikoKeyboardEvent) {
app.onKeyboardEvent(event) app.onKeyboardEvent(event)
} }
......
package org.jetbrains.skiko
actual interface SkikoInput {
/**
* A Boolean value that indicates whether the text-entry object has any text.
* https://developer.apple.com/documentation/uikit/uikeyinput/1614457-hastext
*/
fun hasText(): Boolean
/**
* Inserts a character into the displayed text.
* Add the character text to your class’s backing store at the index corresponding to the cursor and redisplay the text.
* https://developer.apple.com/documentation/uikit/uikeyinput/1614543-inserttext
* @param text A string object representing the character typed on the system keyboard.
*/
fun insertText(text: String)
/**
* Deletes a character from the displayed text.
* Remove the character just before the cursor from your class’s backing store and redisplay the text.
* https://developer.apple.com/documentation/uikit/uikeyinput/1614572-deletebackward
*/
fun deleteBackward()
/**
* The text position for the end of a document.
* https://developer.apple.com/documentation/uikit/uitextinput/1614555-endofdocument
*/
fun endOfDocument(): Long
/**
* The range of selected text in a document.
* If the text range has a length, it indicates the currently selected text.
* If it has zero length, it indicates the caret (insertion point).
* If the text-range object is nil, it indicates that there is no current selection.
* https://developer.apple.com/documentation/uikit/uitextinput/1614541-selectedtextrange
*/
fun getSelectedTextRange(): IntRange?
fun setSelectedTextRange(range: IntRange?)
fun selectAll()
/**
* Returns the text in the specified range.
* https://developer.apple.com/documentation/uikit/uitextinput/1614527-text
* @param range A range of text in a document.
* @return A substring of a document that falls within the specified range.
*/
fun textInRange(range: IntRange): String
/**
* Replaces the text in a document that is in the specified range.
* https://developer.apple.com/documentation/uikit/uitextinput/1614558-replace
* @param range A range of text in a document.
* @param text A string to replace the text in range.
*/
fun replaceRange(range: IntRange, text: String)
/**
* Inserts the provided text and marks it to indicate that it is part of an active input session.
* Setting marked text either replaces the existing marked text or,
* if none is present, inserts it in place of the current selection.
* https://developer.apple.com/documentation/uikit/uitextinput/1614465-setmarkedtext
* @param markedText The text to be marked.
* @param selectedRange A range within markedText that indicates the current selection.
* This range is always relative to markedText.
*/
fun setMarkedText(markedText: String?, selectedRange: IntRange)
/**
* The range of currently marked text in a document.
* If there is no marked text, the value of the property is nil.
* Marked text is provisionally inserted text that requires user confirmation;
* it occurs in multistage text input.
* The current selection, which can be a caret or an extended range, always occurs within the marked text.
* https://developer.apple.com/documentation/uikit/uitextinput/1614489-markedtextrange
*/
fun markedTextRange(): IntRange?
/**
* Unmarks the currently marked text.
* After this method is called, the value of markedTextRange is nil.
* https://developer.apple.com/documentation/uikit/uitextinput/1614512-unmarktext
*/
fun unmarkText()
actual object Empty : SkikoInput {
override fun hasText(): Boolean = false
override fun insertText(text: String) = Unit
override fun deleteBackward() = Unit
override fun endOfDocument(): Long = 0L
override fun getSelectedTextRange(): IntRange? = null
override fun setSelectedTextRange(range: IntRange?) = Unit
override fun selectAll() = Unit
override fun textInRange(range: IntRange): String = ""
override fun replaceRange(range: IntRange, text: String) = Unit
override fun setMarkedText(markedText: String?, selectedRange: IntRange) = Unit
override fun markedTextRange(): IntRange? = null
override fun unmarkText() = Unit
}
}
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlinx.cinterop.CValue import kotlinx.cinterop.*
import kotlinx.cinterop.ExportObjCClass import platform.CoreGraphics.*
import kotlinx.cinterop.useContents import platform.Foundation.*
import platform.CoreGraphics.CGRect import platform.UIKit.*
import platform.CoreGraphics.CGRectMake import platform.darwin.NSInteger
import platform.Foundation.NSCoder import kotlin.math.max
import platform.UIKit.UIEvent import kotlin.math.min
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
@Suppress("CONFLICTING_OVERLOADS")
@ExportObjCClass @ExportObjCClass
class SkikoUIView : UIView, UIKeyInputProtocol { class SkikoUIView : UIView, UIKeyInputProtocol, UITextInputProtocol, UITextPasteConfigurationSupportingProtocol {
@OverrideInit @OverrideInit
constructor(frame: CValue<CGRect>) : super(frame) constructor(frame: CValue<CGRect>) : super(frame)
...@@ -25,11 +18,18 @@ class SkikoUIView : UIView, UIKeyInputProtocol { ...@@ -25,11 +18,18 @@ class SkikoUIView : UIView, UIKeyInputProtocol {
constructor(coder: NSCoder) : super(coder) constructor(coder: NSCoder) : super(coder)
private var skiaLayer: SkiaLayer? = null private var skiaLayer: SkiaLayer? = null
private var _inputDelegate: UITextInputDelegateProtocol? = null
private var _pasteConfiguration: UIPasteConfiguration? = null
private var _pasteDelegate: UITextPasteDelegateProtocol? = null
constructor(skiaLayer: SkiaLayer, frame: CValue<CGRect> = CGRectMake(0.0, 0.0, 1.0, 1.0)) : super(frame) { constructor(skiaLayer: SkiaLayer, frame: CValue<CGRect> = CGRectNull.readValue()) : super(frame) {
this.skiaLayer = skiaLayer this.skiaLayer = skiaLayer
} }
override fun selectAll(sender: Any?) {
skiaLayer?.skikoView?.input?.selectAll()
}
fun detach() = skiaLayer?.detach() fun detach() = skiaLayer?.detach()
fun load(): SkikoUIView { fun load(): SkikoUIView {
...@@ -50,33 +50,31 @@ class SkikoUIView : UIView, UIKeyInputProtocol { ...@@ -50,33 +50,31 @@ class SkikoUIView : UIView, UIKeyInputProtocol {
fun hideScreenKeyboard() = resignFirstResponder() fun hideScreenKeyboard() = resignFirstResponder()
fun isScreenKeyboardOpen() = isFirstResponder fun isScreenKeyboardOpen() = isFirstResponder
private val pressedKeycodes: MutableSet<Long> = mutableSetOf() /**
* A Boolean value that indicates whether the text-entry object has any text.
@Deprecated("need be deleted") * https://developer.apple.com/documentation/uikit/uikeyinput/1614457-hastext
private var inputText: String = ""//todo delete, because it's redundant and may leaks memory */
override fun hasText(): Boolean { override fun hasText(): Boolean {
return inputText.length > 0 return skiaLayer?.skikoView?.input?.hasText() ?: false
} }
override fun insertText(theText: String) { /**
inputText += theText * Inserts a character into the displayed text.
skiaLayer?.skikoView?.onInputEvent(toSkikoTypeEvent(theText, null)) * Add the character text to your class’s backing store at the index corresponding to the cursor and redisplay the text.
* https://developer.apple.com/documentation/uikit/uikeyinput/1614543-inserttext
* @param text A string object representing the character typed on the system keyboard.
*/
override fun insertText(text: String) {
skiaLayer?.skikoView?.input?.insertText(text)
} }
/**
* Deletes a character from the displayed text.
* Remove the character just before the cursor from your class’s backing store and redisplay the text.
* https://developer.apple.com/documentation/uikit/uikeyinput/1614572-deletebackward
*/
override fun deleteBackward() { override fun deleteBackward() {
inputText = inputText.dropLast(1) skiaLayer?.skikoView?.input?.deleteBackward()
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 canBecomeFirstResponder() = true
...@@ -86,9 +84,6 @@ class SkikoUIView : UIView, UIKeyInputProtocol { ...@@ -86,9 +84,6 @@ class SkikoUIView : UIView, UIKeyInputProtocol {
for (press in withEvent.allPresses) { for (press in withEvent.allPresses) {
val uiPress = press as? UIPress val uiPress = press as? UIPress
if (uiPress != null) { if (uiPress != null) {
uiPress.key?.let {
pressedKeycodes.add(it.keyCode)
}
skiaLayer?.skikoView?.onKeyboardEvent( skiaLayer?.skikoView?.onKeyboardEvent(
toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.DOWN) toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.DOWN)
) )
...@@ -103,9 +98,6 @@ class SkikoUIView : UIView, UIKeyInputProtocol { ...@@ -103,9 +98,6 @@ class SkikoUIView : UIView, UIKeyInputProtocol {
for (press in withEvent.allPresses) { for (press in withEvent.allPresses) {
val uiPress = press as? UIPress val uiPress = press as? UIPress
if (uiPress != null) { if (uiPress != null) {
uiPress.key?.let {
pressedKeycodes.remove(it.keyCode)
}
skiaLayer?.skikoView?.onKeyboardEvent( skiaLayer?.skikoView?.onKeyboardEvent(
toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.UP) toSkikoKeyboardEvent(press, SkikoKeyboardEventKind.UP)
) )
...@@ -117,57 +109,358 @@ class SkikoUIView : UIView, UIKeyInputProtocol { ...@@ -117,57 +109,358 @@ class SkikoUIView : UIView, UIKeyInputProtocol {
override fun touchesBegan(touches: Set<*>, withEvent: UIEvent?) { override fun touchesBegan(touches: Set<*>, withEvent: UIEvent?) {
super.touchesBegan(touches, withEvent) super.touchesBegan(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf() sendTouchEventToSkikoView(touches, SkikoTouchEventKind.STARTED)
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?) { override fun touchesEnded(touches: Set<*>, withEvent: UIEvent?) {
super.touchesEnded(touches, withEvent) super.touchesEnded(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf() sendTouchEventToSkikoView(touches, SkikoTouchEventKind.ENDED)
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?) { override fun touchesMoved(touches: Set<*>, withEvent: UIEvent?) {
super.touchesMoved(touches, withEvent) super.touchesMoved(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf() sendTouchEventToSkikoView(touches, SkikoTouchEventKind.MOVED)
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?) { override fun touchesCancelled(touches: Set<*>, withEvent: UIEvent?) {
super.touchesCancelled(touches, withEvent) super.touchesCancelled(touches, withEvent)
val events: MutableList<SkikoTouchEvent> = mutableListOf() sendTouchEventToSkikoView(touches, SkikoTouchEventKind.CANCELLED)
for (touch in touches) { }
val event = touch as UITouch
private fun sendTouchEventToSkikoView(touches: Set<*>, kind: SkikoTouchEventKind) {
val events = touches.map {
val event = it as UITouch
val (x, y) = event.locationInView(null).useContents { x to y } val (x, y) = event.locationInView(null).useContents { x to y }
val timestamp = (event.timestamp * 1_000).toLong() val timestamp = (event.timestamp * 1_000).toLong()
events.add( SkikoTouchEvent(x, y, kind, timestamp, event)
SkikoTouchEvent(x, y, SkikoTouchEventKind.CANCELLED, timestamp, event) }.toTypedArray()
skiaLayer?.skikoView?.onTouchEvent(events)
}
override fun inputDelegate(): UITextInputDelegateProtocol? {
return _inputDelegate
}
override fun setInputDelegate(inputDelegate: UITextInputDelegateProtocol?) {
_inputDelegate = inputDelegate
}
/**
* Returns the text in the specified range.
* https://developer.apple.com/documentation/uikit/uitextinput/1614527-text
* @param range A range of text in a document.
* @return A substring of a document that falls within the specified range.
*/
override fun textInRange(range: UITextRange): String? {
return skiaLayer?.skikoView?.input?.textInRange(range.toIntRange())
}
/**
* Replaces the text in a document that is in the specified range.
* https://developer.apple.com/documentation/uikit/uitextinput/1614558-replace
* @param range A range of text in a document.
* @param text A string to replace the text in range.
*/
override fun replaceRange(range: UITextRange, withText: String) {
skiaLayer?.skikoView?.input?.replaceRange(range.toIntRange(), withText)
}
override fun setSelectedTextRange(selectedTextRange: UITextRange?) {
skiaLayer?.skikoView?.input?.setSelectedTextRange(selectedTextRange?.toIntRange())
}
/**
* The range of selected text in a document.
* If the text range has a length, it indicates the currently selected text.
* If it has zero length, it indicates the caret (insertion point).
* If the text-range object is nil, it indicates that there is no current selection.
* https://developer.apple.com/documentation/uikit/uitextinput/1614541-selectedtextrange
*/
override fun selectedTextRange(): UITextRange? {
return skiaLayer?.skikoView?.input?.getSelectedTextRange()?.toUITextRange()
}
/**
* The range of currently marked text in a document.
* If there is no marked text, the value of the property is nil.
* Marked text is provisionally inserted text that requires user confirmation;
* it occurs in multistage text input.
* The current selection, which can be a caret or an extended range, always occurs within the marked text.
* https://developer.apple.com/documentation/uikit/uitextinput/1614489-markedtextrange
*/
override fun markedTextRange(): UITextRange? {
return skiaLayer?.skikoView?.input?.markedTextRange()?.toUITextRange()
}
override fun setMarkedTextStyle(markedTextStyle: Map<Any?, *>?) {
// do nothing
}
override fun markedTextStyle(): Map<Any?, *>? {
return null
}
/**
* Inserts the provided text and marks it to indicate that it is part of an active input session.
* Setting marked text either replaces the existing marked text or,
* if none is present, inserts it in place of the current selection.
* https://developer.apple.com/documentation/uikit/uitextinput/1614465-setmarkedtext
* @param markedText The text to be marked.
* @param selectedRange A range within markedText that indicates the current selection.
* This range is always relative to markedText.
*/
override fun setMarkedText(markedText: String?, selectedRange: CValue<NSRange>) {
val (locationRelative, lengthRelative) = selectedRange.useContents {
location.toInt() to length.toInt()
}
val relativeTextRange = locationRelative until locationRelative + lengthRelative
skiaLayer?.skikoView?.input?.setMarkedText(markedText, relativeTextRange)
}
/**
* Unmarks the currently marked text.
* After this method is called, the value of markedTextRange is nil.
* https://developer.apple.com/documentation/uikit/uitextinput/1614512-unmarktext
*/
override fun unmarkText() {
skiaLayer?.skikoView?.input?.unmarkText()
}
override fun beginningOfDocument(): UITextPosition {
return IntermediateTextPosition(0)
}
/**
* The text position for the end of a document.
* https://developer.apple.com/documentation/uikit/uitextinput/1614555-endofdocument
*/
override fun endOfDocument(): UITextPosition {
return IntermediateTextPosition(skiaLayer?.skikoView?.input?.endOfDocument() ?: 0)
}
/**
* Attention! fromPosition and toPosition may be null
*/
override fun textRangeFromPosition(fromPosition: UITextPosition, toPosition: UITextPosition): UITextRange? {
val from = (fromPosition as? IntermediateTextPosition)?.position ?: 0
val to = (toPosition as? IntermediateTextPosition)?.position ?: 0
return IntermediateTextRange(
IntermediateTextPosition(minOf(from, to)),
IntermediateTextPosition(maxOf(from, to))
) )
} }
skiaLayer?.skikoView?.onTouchEvent(events.toTypedArray())
override fun positionFromPosition(position: UITextPosition, offset: NSInteger): UITextPosition? {
val p = (position as IntermediateTextPosition).position
val endOfDocument = skiaLayer?.skikoView?.input?.endOfDocument()
return if (endOfDocument != null) {
IntermediateTextPosition(max(min(p + offset, endOfDocument), 0))
} else {
null
}
}
override fun positionFromPosition(
position: UITextPosition,
inDirection: UITextLayoutDirection,
offset: NSInteger
): UITextPosition? {
return when (inDirection) {
UITextLayoutDirectionLeft, UITextLayoutDirectionUp -> {
positionFromPosition(position, -offset)
}
else -> positionFromPosition(position, offset)
}
}
/**
* Attention! position and toPosition may be null
*/
override fun comparePosition(position: UITextPosition, toPosition: UITextPosition): NSComparisonResult {
val from = (position as? IntermediateTextPosition)?.position ?: 0
val to = (toPosition as? IntermediateTextPosition)?.position ?: 0
val result = if (from < to) {
NSOrderedAscending
} else if (from > to) {
NSOrderedDescending
} else {
NSOrderedSame
}
return result
}
override fun offsetFromPosition(from: UITextPosition, toPosition: UITextPosition): NSInteger {
val fromPosition = from as IntermediateTextPosition
val to = toPosition as IntermediateTextPosition
return to.position - fromPosition.position
}
override fun tokenizer(): UITextInputTokenizerProtocol {
return UITextInputStringTokenizer()
}
override fun positionWithinRange(range: UITextRange, atCharacterOffset: NSInteger): UITextPosition? =
TODO("positionWithinRange range: $range, atCharacterOffset: $atCharacterOffset")
override fun positionWithinRange(range: UITextRange, farthestInDirection: UITextLayoutDirection): UITextPosition? =
TODO("positionWithinRange, farthestInDirection: ${farthestInDirection.directionToStr()}")
override fun characterRangeByExtendingPosition(
position: UITextPosition,
inDirection: UITextLayoutDirection
): UITextRange? {
TODO("characterRangeByExtendingPosition, inDirection: ${inDirection.directionToStr()}")
}
override fun baseWritingDirectionForPosition(
position: UITextPosition,
inDirection: UITextStorageDirection
): NSWritingDirection {
return NSWritingDirectionLeftToRight // TODO support RTL text direction
}
override fun setBaseWritingDirection(writingDirection: NSWritingDirection, forRange: UITextRange) {
// TODO support RTL text direction
}
//Working with Geometry and Hit-Testing. All methods return stubs for now.
override fun firstRectForRange(range: UITextRange): CValue<CGRect> = CGRectNull.readValue()
override fun caretRectForPosition(position: UITextPosition): CValue<CGRect> = CGRectNull.readValue()
override fun selectionRectsForRange(range: UITextRange): List<*> = listOf<UITextSelectionRect>()
override fun closestPositionToPoint(point: CValue<CGPoint>): UITextPosition? = null
override fun closestPositionToPoint(point: CValue<CGPoint>, withinRange: UITextRange): UITextPosition? = null
override fun characterRangeAtPoint(point: CValue<CGPoint>): UITextRange? = null
override fun textStylingAtPosition(position: UITextPosition, inDirection: UITextStorageDirection): Map<Any?, *>? {
return NSDictionary.dictionary()
}
override fun characterOffsetOfPosition(position: UITextPosition, withinRange: UITextRange): NSInteger {
TODO("characterOffsetOfPosition")
}
override fun shouldChangeTextInRange(range: UITextRange, replacementText: String): Boolean {
// Here we should decide to replace text in range or not.
// By default, this method returns true.
return true
}
override fun textInputView(): UIView {
return this
}
override fun canPerformAction(action: COpaquePointer?, withSender: Any?): Boolean {
//todo context menu with actions
return true
}
override fun pasteConfiguration(): UIPasteConfiguration? {
//https://developer.apple.com/documentation/uikit/uitextpasteconfigurationsupporting
//todo uikit copy/paste
return _pasteConfiguration
}
override fun setPasteConfiguration(pasteConfiguration: UIPasteConfiguration?) {
//todo uikit copy/paste
_pasteConfiguration = pasteConfiguration
}
override fun pasteDelegate(): UITextPasteDelegateProtocol? {
//todo uikit copy/paste
return _pasteDelegate
}
override fun setPasteDelegate(pasteDelegate: UITextPasteDelegateProtocol?) {
//todo uikit copy/paste
_pasteDelegate = pasteDelegate
}
override fun keyboardType(): UIKeyboardType {
return UIKeyboardTypeDefault //todo keyboardType
}
override fun isSecureTextEntry(): Boolean {
return false //todo secure text to prevent copy
}
override fun autocapitalizationType(): UITextAutocapitalizationType {
return UITextAutocapitalizationType.UITextAutocapitalizationTypeSentences
// return UITextAutocapitalizationType.UITextAutocapitalizationTypeAllCharacters
}
override fun autocorrectionType(): UITextAutocorrectionType {
return UITextAutocorrectionType.UITextAutocorrectionTypeYes
}
override fun dictationRecognitionFailed() {
//todo may be useful
}
override fun dictationRecordingDidEnd() {
//todo may be useful
}
/**
* Call when something changes in text data
*/
fun textWillChange() {
_inputDelegate?.textWillChange(this)
}
/**
* Call when something changes in text data
*/
fun textDidChange() {
_inputDelegate?.textDidChange(this)
}
/**
* Call when something changes in text data
*/
fun selectionWillChange() {
_inputDelegate?.selectionWillChange(this)
}
/**
* Call when something changes in text data
*/
fun selectionDidChange() {
_inputDelegate?.selectionDidChange(this)
} }
} }
private class IntermediateTextPosition(val position: Long = 0) : UITextPosition()
private fun IntermediateTextRange(start: Int, end: Int) =
IntermediateTextRange(
_start = IntermediateTextPosition(start.toLong()),
_end = IntermediateTextPosition(end.toLong())
)
private class IntermediateTextRange(
private val _start: IntermediateTextPosition,
private val _end: IntermediateTextPosition
) : UITextRange() {
override fun isEmpty() = (_end.position - _start.position) <= 0
override fun start(): UITextPosition = _start
override fun end(): UITextPosition = _end
}
private fun UITextRange.toIntRange(): IntRange {
val start = (start() as IntermediateTextPosition).position.toInt()
val end = (end() as IntermediateTextPosition).position.toInt()
return start until end
}
private fun IntRange.toUITextRange(): UITextRange =
IntermediateTextRange(start = start, end = endInclusive + 1)
private fun NSWritingDirection.directionToStr() =
when (this) {
UITextLayoutDirectionLeft -> "Left"
UITextLayoutDirectionRight -> "Right"
UITextLayoutDirectionUp -> "Up"
UITextLayoutDirectionDown -> "Down"
else -> "unknown direction"
}
...@@ -135,7 +135,7 @@ actual open class SkiaLayer { ...@@ -135,7 +135,7 @@ actual open class SkiaLayer {
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN)) skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.DOWN))
toSkikoTypeEvent(event.key, event)?.let { inputEvent -> toSkikoTypeEvent(event.key, event)?.let { inputEvent ->
skikoView?.onInputEvent(inputEvent) skikoView?.input?.onInputEvent(inputEvent)
} }
}) })
htmlCanvas.addEventListener("keyup", { event -> htmlCanvas.addEventListener("keyup", { event ->
......
package org.jetbrains.skiko
actual interface SkikoInput {
fun onInputEvent(event: SkikoInputEvent)
actual object Empty : SkikoInput {
override fun onInputEvent(event: SkikoInputEvent) = Unit
}
}
package org.jetbrains.skiko
actual interface SkikoInput {
actual object Empty : SkikoInput
}
package org.jetbrains.skiko
actual interface SkikoInput {
actual object Empty : SkikoInput
}
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