diff --git a/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/misc/ParamsProvider.kt b/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/misc/ParamsProvider.kt index e811dd1ab..889951a06 100644 --- a/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/misc/ParamsProvider.kt +++ b/projector-client-common/src/jsMain/kotlin/org/jetbrains/projector/client/common/misc/ParamsProvider.kt @@ -66,6 +66,7 @@ actual object ParamsProvider { private const val DEFAULT_REPAINT_INTERVAL_MS = 333 private const val DEFAULT_IMAGE_CACHE_SIZE_CHARS = 5_000_000 private const val DEFAULT_BLOCK_CLOSING = true + private const val DEFAULT_SPECULATIVE_TYPING_LATENCY = 0 val SYSTEM_SCALING_RATIO get() = window.devicePixelRatio // get every time because it can be changed @@ -101,6 +102,7 @@ actual object ParamsProvider { actual val IMAGE_CACHE_SIZE_CHARS: Int val BLOCK_CLOSING: Boolean val LAYOUT_TYPE: LayoutType + val SPECULATIVE_TYPING_LATENCY: Int val SCALING_RATIO: Double get() = SYSTEM_SCALING_RATIO * USER_SCALING_RATIO @@ -162,6 +164,7 @@ actual object ParamsProvider { "frAzerty" -> LayoutType.FR_AZERTY else -> LayoutType.JS_DEFAULT } + SPECULATIVE_TYPING_LATENCY = searchParams.get("speculativeTypingLatency")?.toIntOrNull() ?: DEFAULT_SPECULATIVE_TYPING_LATENCY } } diff --git a/projector-client-web/README.md b/projector-client-web/README.md index f4a3a7b6f..a6866db97 100644 --- a/projector-client-web/README.md +++ b/projector-client-web/README.md @@ -66,6 +66,7 @@ Name | Type | Default value | Description `cacheSize` | Int | `5M` | Set size of cache for images in Chars. `blockClosing` | Boolean | `true` | Enable blocking of accidental closing of the web page `relayServerId` | String? | Not present | Identifier of Projector server to connect to for relay connection. Warning: Static files must be accessed via https when relay is used. +`speculativeTypingLatency` | Int | `0` | Sets latency before key press event is sent to server if speculative symbol for the event was drawn. ## Shortcuts - `Ctrl + F10` prints statistics to the browser console. Example: diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/speculative/Typing.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/speculative/Typing.kt index 76c6ffd05..0924e3225 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/speculative/Typing.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/speculative/Typing.kt @@ -39,7 +39,7 @@ sealed class Typing { abstract fun removeSpeculativeImage() - abstract fun addEventChar(event: ClientKeyPressEvent) + abstract fun addEventChar(event: ClientKeyPressEvent): Boolean abstract fun dispose() @@ -53,8 +53,8 @@ sealed class Typing { // do nothing } - override fun addEventChar(event: ClientKeyPressEvent) { - // do nothing + override fun addEventChar(event: ClientKeyPressEvent): Boolean { + return false } override fun dispose() { @@ -124,14 +124,14 @@ sealed class Typing { || event.char.category.fromOtherUnicodeGroup } - override fun addEventChar(event: ClientKeyPressEvent) { - if (shouldSkipEvent(event)) return + override fun addEventChar(event: ClientKeyPressEvent): Boolean { + if (shouldSkipEvent(event)) return false - val currentCarets = carets as? ServerCaretInfoChangedEvent.CaretInfoChange.Carets ?: return + val currentCarets = carets as? ServerCaretInfoChangedEvent.CaretInfoChange.Carets ?: return false - val canvas = canvasByIdGetter(currentCarets.editorWindowId) ?: return + val canvas = canvasByIdGetter(currentCarets.editorWindowId) ?: return false - val firstCaretLocation = currentCarets.caretInfoList.firstOrNull()?.locationInWindow ?: return // todo: support multiple carets + val firstCaretLocation = currentCarets.caretInfoList.firstOrNull()?.locationInWindow ?: return false // todo: support multiple carets ensureSpeculativeCanvasSize(canvas) @@ -182,6 +182,8 @@ sealed class Typing { speculativeCanvasImage.style.display = "block" canvas.style.display = "none" + + return true } private fun ensureSpeculativeCanvasSize(canvas: HTMLCanvasElement) { diff --git a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt index b886035e9..428833d9e 100644 --- a/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt +++ b/projector-client-web/src/main/kotlin/org/jetbrains/projector/client/web/state/ClientState.kt @@ -508,12 +508,25 @@ sealed class ClientState { is ClientAction.AddEvent -> { val event = action.event + fun addEvent() { + eventsToSend.add(event) + messagingPolicy.onAddEvent() + } + + var latency = 0 + if (event is ClientKeyPressEvent) { - typing.addEventChar(event) + val added = typing.addEventChar(event) + if (added) { + latency = ParamsProvider.SPECULATIVE_TYPING_LATENCY + } } - eventsToSend.add(event) - messagingPolicy.onAddEvent() + if (latency > 0) { + window.setTimeout(::addEvent, latency) + } else { + addEvent() + } this }