From 6faab0a27cdd933e2398c02f4efcf9e19e06a849 Mon Sep 17 00:00:00 2001 From: Carlos Ballesteros Velasco Date: Thu, 13 Jun 2024 16:08:55 +0200 Subject: [PATCH] Bump korlibs to 6.0.0-alpha2 (#2233) --- .github/workflows/TEST.yml | 2 +- .gitmodules | 3 - .../korlibs/korge/gradle/util/ASEInfo.kt | 2 +- e2e/e2e-test/src/commonMain/kotlin/RefMain.kt | 1 + gradle/libs.versions.toml | 28 +-- .../com/soywiz/kproject/model/Dependency.kt | 5 +- .../com/soywiz/kproject/model/FileRef.kt | 5 + .../NewKProjectGradleGeneratorTest_test.txt | 2 +- korge-korlibs | 1 - korge-sandbox/src/samples/MainBezier.kt | 2 + korge-sandbox/src/samples/MainFilterSwitch.kt | 1 + korge-sandbox/src/samples/MainFilters.kt | 3 +- .../src/samples/MainFiltersRenderToBitmap.kt | 2 +- .../src/samples/MainFiltersSample.kt | 3 +- korge-sandbox/src/samples/MainFlag.kt | 3 +- korge-sandbox/src/samples/MainPolyphonic.kt | 14 +- korge-sandbox/src/samples/MainShapes.kt | 2 + korge-sandbox/src/samples/MainTextMetrics.kt | 1 + korge-sandbox/src/samples/MainTweenPoint.kt | 2 + korge-sandbox/src/samples/asteroids/Game.kt | 1 + .../src/samples/minesweeper/Process.kt | 167 +++++++++--------- korge/build.gradle.kts | 3 + .../datastructure/_Datastructure_event.kt | 36 ++-- .../datastructure/closeable/Closeable.kt | 4 + .../korlibs/datastructure/lock/LockAlias.kt | 7 + .../datastructure/thread/ThreadAlias.kt | 13 ++ korge/src/korlibs/event/EventListener.kt | 13 +- .../gamepad/X11JoyGameControllerCommon.kt | 15 +- korge/src/korlibs/graphics/AGObjects.kt | 6 +- korge/src/korlibs/graphics/gl/AGOpengl.kt | 11 +- korge/src/korlibs/graphics/shader/shaders.kt | 2 +- korge/src/korlibs/io/annotations/Keep.kt | 4 + .../src/korlibs/io/annotations/KorInternal.kt | 3 + korge/src/korlibs/io/async/AsyncAlias.kt | 3 + korge/src/korlibs/io/lang/Disposable.kt | 8 + korge/src/korlibs/kgl/KmlGlContext.kt | 2 +- korge/src/korlibs/korge/Korge.kt | 3 +- korge/src/korlibs/korge/animate/Animator.kt | 161 ++++++++++++----- .../korge/animate/AnimatorStateManager.kt | 10 +- korge/src/korlibs/korge/audio/AudioChannel.kt | 5 +- .../korlibs/korge/component/StageComponent.kt | 2 +- korge/src/korlibs/korge/input/DropFile.kt | 2 +- korge/src/korlibs/korge/input/Input.kt | 11 +- korge/src/korlibs/korge/input/KeysEvents.kt | 94 +++++++--- .../korlibs/korge/input/MouseDragComponent.kt | 29 +-- korge/src/korlibs/korge/input/MouseEvents.kt | 19 +- .../korlibs/korge/render/AgAutoFreeManager.kt | 8 +- .../korge/render/AgBitmapTextureManager.kt | 2 +- .../src/korlibs/korge/render/RenderContext.kt | 4 +- korge/src/korlibs/korge/render/Texture.kt | 2 +- .../korlibs/korge/scene/CompletableScene.kt | 3 +- korge/src/korlibs/korge/scene/Scene.kt | 3 +- .../src/korlibs/korge/scene/SceneContainer.kt | 47 ++--- .../service/vibration/NativeVibration.kt | 5 +- korge/src/korlibs/korge/style/ViewStyles.kt | 4 +- .../korge/tests/TestCoroutineDispatcher.kt | 5 +- .../korlibs/korge/tests/ViewsForTesting.kt | 11 +- .../korlibs/korge/text/TextEditController.kt | 2 +- korge/src/korlibs/korge/time/TimeSpanExt.kt | 3 +- .../src/korlibs/korge/time/TimerComponents.kt | 27 +-- korge/src/korlibs/korge/tween/tween.kt | 43 ++--- korge/src/korlibs/korge/tween/tweenbase.kt | 161 +++++++++++------ korge/src/korlibs/korge/ui/UIScrollable.kt | 41 +++-- .../korlibs/korge/ui/UITooltipContainer.kt | 22 ++- .../korge/ui/UITooltipContainerMediatorNew.kt | 2 +- .../src/korlibs/korge/view/CachedContainer.kt | 2 +- korge/src/korlibs/korge/view/Camera.kt | 5 +- korge/src/korlibs/korge/view/FpsOverlay.kt | 12 +- korge/src/korlibs/korge/view/Sprite.kt | 53 +++--- .../src/korlibs/korge/view/SpriteAnimation.kt | 7 +- korge/src/korlibs/korge/view/View.kt | 15 +- korge/src/korlibs/korge/view/Views.kt | 17 +- korge/src/korlibs/korge/view/camera/Camera.kt | 5 +- .../korlibs/korge/view/filter/FlagFilter.kt | 7 +- .../korge/view/filter/SwizzleColorFilter.kt | 2 +- .../korlibs/korge/view/filter/WaveFilter.kt | 3 +- korge/src/korlibs/render/GameWindow.kt | 46 +++-- .../render/GameWindowCoroutineDispatcher.kt | 14 +- .../SyncEventLoopCoroutineDispatcher.kt | 3 +- .../service/vibration/NativeVibration.kt | 5 +- .../korlibs/render/DefaultGameWindowIos.kt | 6 + .../datastructure/event/JsEventLoop.kt | 16 +- .../korge/service/storage/NativeStorage.kt | 4 +- korge/src@jvm/korlibs/korge/awt/UIAwt.kt | 1 - .../datastructure/event/WasmJsEventLoop.kt | 16 +- korge/src@wasmJs/korlibs/graphics/gl/GlExt.kt | 3 +- .../src@wasmJs/korlibs/kgl/KmlGlWasmCanvas.kt | 1 + .../service/vibration/NativeVibration.kt | 2 +- .../korlibs/render/DefaultGameWindowWasm.kt | 3 + .../render/internal/KorgwWasmInternal.kt | 33 ++++ .../datastructure/event/SyncEventLoopTest.kt | 10 +- .../korlibs/korge/input/KeysEventsTest.kt | 3 +- .../event/JvmSyncEventLoopTest.kt | 4 + .../korlibs/render/awt/AwtGameCanvasTest.kt | 4 +- .../korlibs/render/awt/AwtGameWindowTest.kt | 3 + 95 files changed, 881 insertions(+), 535 deletions(-) delete mode 100644 .gitmodules delete mode 160000 korge-korlibs create mode 100644 korge/src/korlibs/datastructure/closeable/Closeable.kt create mode 100644 korge/src/korlibs/datastructure/lock/LockAlias.kt create mode 100644 korge/src/korlibs/datastructure/thread/ThreadAlias.kt create mode 100644 korge/src/korlibs/io/annotations/Keep.kt create mode 100644 korge/src/korlibs/io/annotations/KorInternal.kt create mode 100644 korge/src/korlibs/io/async/AsyncAlias.kt create mode 100644 korge/src/korlibs/io/lang/Disposable.kt create mode 100644 korge/src@wasmJs/korlibs/render/internal/KorgwWasmInternal.kt diff --git a/.github/workflows/TEST.yml b/.github/workflows/TEST.yml index 4264d454db..011405383d 100644 --- a/.github/workflows/TEST.yml +++ b/.github/workflows/TEST.yml @@ -26,7 +26,7 @@ jobs: matrix: include: - { outputKey: testIos, os: macos-11, testTask: iosX64Test, precompileTask: compileTestKotlinIosX64, enableKotlinNative: true } - - { outputKey: testJs, os: ubuntu-latest, testTask: "wasmJsBrowserTest", buildTasks: "jsNodeTest jsBrowserTest", precompileTask: "wasmJsTestClasses jsTestClasses" } + - { outputKey: testJs, os: ubuntu-latest, testTask: "wasmJsBrowserTest", buildTasks: "jsBrowserTest", precompileTask: "wasmJsTestClasses jsTestClasses" } - { outputKey: testAndroid, os: ubuntu-latest, enableAndroid: true, precompileTask: "compileDebugAndroidTestSources" } - { outputKey: testJvmMacos, os: macos-11, testTask: jvmTest, precompileTask: "compileTestKotlinJvm compileTestKotlin" } - { outputKey: testJvmLinux, os: ubuntu-latest, testTask: jvmTest, precompileTask: "compileTestKotlinJvm compileTestKotlin", enableKotlinNative: true, enableSandbox: true, e2e: true } diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e42e9efcb3..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "korge-korlibs"] - path = korge-korlibs - url = https://github.com/korlibs/korge-korlibs.git diff --git a/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/ASEInfo.kt b/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/ASEInfo.kt index 9b61907495..66faebd661 100644 --- a/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/ASEInfo.kt +++ b/buildSrc/src/main/kotlin/korlibs/korge/gradle/util/ASEInfo.kt @@ -34,7 +34,7 @@ data class ASEInfo( val tags = arrayListOf() val fileSize = s.readS32LE() - if (s.length < fileSize) error("File too short") + if (s.length < fileSize) error("File too short s.length=${s.length} < fileSize=${fileSize}") val headerMagic = s.readU16LE() if (headerMagic != 0xA5E0) error("Not an Aseprite file : headerMagic=$headerMagic") val numFrames = s.readU16LE() diff --git a/e2e/e2e-test/src/commonMain/kotlin/RefMain.kt b/e2e/e2e-test/src/commonMain/kotlin/RefMain.kt index b2dacee0db..d1469b0b44 100644 --- a/e2e/e2e-test/src/commonMain/kotlin/RefMain.kt +++ b/e2e/e2e-test/src/commonMain/kotlin/RefMain.kt @@ -12,6 +12,7 @@ import korlibs.io.lang.* import korlibs.io.serialization.json.Json import korlibs.math.geom.* import korlibs.math.interpolation.* +import korlibs.io.file.jail suspend fun main() = Korge(windowSize = Size(768, 512), backgroundColor = Colors["#2b2b2b"]) { //println("StandardPaths.cwd=${korlibs.io.file.std.StandardPaths.cwd}") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a31fee496..d3c0203ecb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,38 +10,21 @@ asm = "9.5" jgit = "5.13.1.202206130422-r" kotlin = "2.0.0" -kotlinx-coroutines = "1.8.0" -kotlinx-serialization = "1.6.2" -kotlinx-atomicfu = "0.23.1" +#kotlinx-coroutines = "1.8.1" +kotlinx-coroutines = "1.9.0-RC" +kotlinx-serialization = "1.7.0" +kotlinx-atomicfu = "0.24.0" -korlibs = "5.5.0-alpha3" - -#maven { url = uri("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") } - -# https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/org/jetbrains/kotlin/kotlin-stdlib/ -#kotlin = "1.9.0-dev-6976" -#kotlin = "1.9.20-dev-2332" -#kotlin = "1.9.20-dev-2914" -# https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental/org/jetbrains/kotlinx/atomicfu/ -# https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental/org/jetbrains/kotlinx/kotlinx-coroutines-core/ -#kotlinx-coroutines = "1.7.2-wasm1" -#kotlinx-serialization = "1.6.0-wasm0" +korlibs = "6.0.0-alpha2" kotlinx-benchmark = "0.4.7" dokka = "1.9.10" kover = "0.6.1" kover-agent = "1.0.712" node = "20.12.1" -#android-build-gradle = "7.4.2" -#android-build-gradle = "8.0.0" -#android-build-gradle = "7.4.0-beta02" -#android-build-gradle = "7.3.1" -#android-build-gradle = "8.1.0-alpha04" -#android-build-gradle = "8.2.0" # The project is using an incompatible version (AGP 8.2.0) of the Android Gradle plugin. Latest supported version is AGP 8.2.0-beta05 android-build-gradle = "8.2.0" gson = "2.10.1" gradle-publish-plugin = "1.1.0" -#gradle-publish-plugin = "0.14.0" closure-compiler = "v20210808" # https://repo.maven.apache.org/maven2/org/jetbrains/intellij/deps/intellij-coverage-agent/ @@ -50,6 +33,7 @@ korlibs-audio = { module = "com.soywiz:korlibs-audio", version.ref = "korlibs" } korlibs-image = { module = "com.soywiz:korlibs-image", version.ref = "korlibs" } korlibs-inject = { module = "com.soywiz:korlibs-inject", version.ref = "korlibs" } korlibs-template = { module = "com.soywiz:korlibs-template", version.ref = "korlibs" } +korlibs-time = { module = "com.soywiz:korlibs-time", version.ref = "korlibs" } korlibs-serialization-yaml = { module = "com.soywiz:korlibs-serialization-yaml", version.ref = "korlibs" } jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" } diff --git a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/Dependency.kt b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/Dependency.kt index 28f369da35..693322a219 100644 --- a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/Dependency.kt +++ b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/Dependency.kt @@ -75,7 +75,7 @@ private val GITHUB_TREE_REGEX = Regex("(https://github\\.com\\/.*?\\/.*?)\\/tree fun Dependency.Companion.parseString(str: String, projectFile: FileRef = MemoryFileRef()): Dependency { try { - val parts = str.split("::") + val parts = str.split(Regex("(::|##)")) val firstPart = parts.first() when (firstPart) { // - git::adder::korlibs/kproject::/modules/adder::54f73b01cea9cb2e8368176ac45f2fca948e57db @@ -133,7 +133,8 @@ fun Dependency.Companion.parseString(str: String, projectFile: FileRef = MemoryF - git@github.com:korlibs/korge-ext.git/korge-tiled#0.0.1::734d96ccc18733064ef9fbda8ac359585011112d - "https://github.com/korlibs/korge-ext.git/korge-tiled#0.0.1::734d96ccc18733064ef9fbda8ac359585011112d" - "https://github.com/korlibs/korge-parallax/tree/0.0.1/korge-parallax::dacd7f4c430c48349565295394f723b05841c54a" - + - https://github.com/korlibs/korge-virtualcontroller/tree/v1.0.2/korge-virtualcontroller##df0e840b8171bb3f5b8f7a86b77fbe39e725be16 + ## MAVEN: - maven::common::org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4 - com.soywiz.korlibs.korge2:korge diff --git a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/FileRef.kt b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/FileRef.kt index a67b433d53..23406dda84 100644 --- a/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/FileRef.kt +++ b/korge-gradle-plugin-common/src/main/kotlin/com/soywiz/kproject/model/FileRef.kt @@ -75,6 +75,11 @@ data class LocalFileRef(val file: File) : FileRef { } data class GitFileRef(val git: GitRepository, val ref: String, val path: String) : FileRef { + init { + //println("GitFileRef(ref='$ref', path='$path')") + //check(!ref.contains("#")) { "Ref contains #" } + } + override val name: String get() = PathInfo(path).name override fun writeBytes(data: ByteArray) = TODO() override fun readBytes(): ByteArray = git.useGit { it.readFile(ref, path) } diff --git a/korge-gradle-plugin-common/src/test/resources/NewKProjectGradleGeneratorTest_test.txt b/korge-gradle-plugin-common/src/test/resources/NewKProjectGradleGeneratorTest_test.txt index 94b5e5f43e..0ae384b65e 100644 --- a/korge-gradle-plugin-common/src/test/resources/NewKProjectGradleGeneratorTest_test.txt +++ b/korge-gradle-plugin-common/src/test/resources/NewKProjectGradleGeneratorTest_test.txt @@ -32,7 +32,7 @@ plugins { id("org.jetbrains.kotlin.plugin.serialization") } dependencies { - add("commonMainApi", "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") + add("commonMainApi", "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0") add("commonMainApi", project(":mymodule")) } [file("build.extra.gradle"), file("build.extra.gradle.kts")].each { extraGradle -> diff --git a/korge-korlibs b/korge-korlibs deleted file mode 160000 index de91b02a15..0000000000 --- a/korge-korlibs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit de91b02a15ccea72371f4e5a70ecd72303264ad1 diff --git a/korge-sandbox/src/samples/MainBezier.kt b/korge-sandbox/src/samples/MainBezier.kt index 086d7b7144..0815997dd7 100644 --- a/korge-sandbox/src/samples/MainBezier.kt +++ b/korge-sandbox/src/samples/MainBezier.kt @@ -3,6 +3,7 @@ package samples import korlibs.image.color.* import korlibs.image.vector.* import korlibs.io.async.* +import korlibs.io.async.launch import korlibs.korge.scene.* import korlibs.korge.tween.* import korlibs.korge.view.* @@ -11,6 +12,7 @@ import korlibs.math.geom.bezier.* import korlibs.math.interpolation.* import korlibs.math.random.* import korlibs.time.* +import kotlinx.coroutines.* import kotlin.random.* class MainBezier : Scene() { diff --git a/korge-sandbox/src/samples/MainFilterSwitch.kt b/korge-sandbox/src/samples/MainFilterSwitch.kt index 6325f1616b..1cb69e9cda 100644 --- a/korge-sandbox/src/samples/MainFilterSwitch.kt +++ b/korge-sandbox/src/samples/MainFilterSwitch.kt @@ -15,6 +15,7 @@ import korlibs.image.format.readBitmap import korlibs.io.async.launch import korlibs.io.async.launchImmediately import korlibs.io.file.std.resourcesVfs +import kotlinx.coroutines.* import util.* import kotlin.random.Random diff --git a/korge-sandbox/src/samples/MainFilters.kt b/korge-sandbox/src/samples/MainFilters.kt index e17052347f..2d99968156 100644 --- a/korge-sandbox/src/samples/MainFilters.kt +++ b/korge-sandbox/src/samples/MainFilters.kt @@ -7,6 +7,7 @@ import korlibs.korge.scene.* import korlibs.korge.tween.* import korlibs.korge.view.* import korlibs.korge.view.filter.* +import korlibs.math.geom.* import korlibs.math.interpolation.* import korlibs.time.* @@ -14,7 +15,7 @@ class MainFilters : ScaledScene(768, 512) { override suspend fun SContainer.sceneMain() { val bitmap = resourcesVfs["korge.png"].readBitmap() - val wave = WaveFilter() + val wave = WaveFilter(crestDistance = Vector2D(200, 200), amplitude = Vector2D(24, 24)) image(bitmap) { scale(.5) position(0, 0) diff --git a/korge-sandbox/src/samples/MainFiltersRenderToBitmap.kt b/korge-sandbox/src/samples/MainFiltersRenderToBitmap.kt index 5fb4bab948..c58c3de199 100644 --- a/korge-sandbox/src/samples/MainFiltersRenderToBitmap.kt +++ b/korge-sandbox/src/samples/MainFiltersRenderToBitmap.kt @@ -16,7 +16,7 @@ class MainFiltersRenderToBitmap : Scene() { val container = FixedSizeContainer(Size(width, height)).apply { //scale(2.0, 2.0) println("PREPARING VIEWS...") - image(bitmap).scale(.5).position(0, 0).addFilter(WaveFilter(time = 0.5.seconds)) + image(bitmap).scale(.5).position(0, 0).addFilter(WaveFilter(time = 0.5.seconds, crestDistance = Vector2D(200, 200), amplitude = Vector2D(24, 24))) //image(bitmap).scale(.5).position(256, 0).addFilter(DirectionalBlurFilter(radius = 32.0)) image(bitmap).scale(.5).position(256, 0).addFilter(BlurFilter(radius = 32.0)) image(bitmap).scale(.5).position(512, 0).addFilter(TransitionFilter(TransitionFilter.Transition.SWEEP, reversed = false, spread = 1.0, ratio = Ratio.HALF)) diff --git a/korge-sandbox/src/samples/MainFiltersSample.kt b/korge-sandbox/src/samples/MainFiltersSample.kt index 5e738d2668..3ed0afd9be 100644 --- a/korge-sandbox/src/samples/MainFiltersSample.kt +++ b/korge-sandbox/src/samples/MainFiltersSample.kt @@ -7,6 +7,7 @@ import korlibs.korge.scene.* import korlibs.korge.tween.* import korlibs.korge.view.* import korlibs.korge.view.filter.* +import korlibs.math.geom.* import korlibs.math.interpolation.* import korlibs.time.* @@ -14,7 +15,7 @@ class MainFiltersSample : Scene() { override suspend fun SContainer.sceneMain() { val bitmap = resourcesVfs["korge.png"].readBitmap() - val wave = WaveFilter() + val wave = WaveFilter(crestDistance = Vector2D(200, 200), amplitude = Vector2D(24, 24)) image(bitmap) { scale(.5) position(0, 0) diff --git a/korge-sandbox/src/samples/MainFlag.kt b/korge-sandbox/src/samples/MainFlag.kt index df854ab24a..468d1a2370 100644 --- a/korge-sandbox/src/samples/MainFlag.kt +++ b/korge-sandbox/src/samples/MainFlag.kt @@ -9,6 +9,7 @@ import korlibs.korge.scene.* import korlibs.korge.view.* import korlibs.korge.view.filter.* import korlibs.time.* +import kotlin.time.* class MainFlag : ScaledScene(592, 592) { override suspend fun SContainer.sceneMain() { @@ -31,7 +32,7 @@ class MainFlag : ScaledScene(592, 592) { } // Propagates the wave over time - addUpdater { dt: TimeSpan -> + addUpdater { dt: Duration -> //println("MainFlag.addUpdater: dt=$dt") flagFilter.time = flagFilter.time.plus(dt) invalidateRender() diff --git a/korge-sandbox/src/samples/MainPolyphonic.kt b/korge-sandbox/src/samples/MainPolyphonic.kt index 4513d2e948..5a2a852829 100644 --- a/korge-sandbox/src/samples/MainPolyphonic.kt +++ b/korge-sandbox/src/samples/MainPolyphonic.kt @@ -6,6 +6,7 @@ import korlibs.korge.scene.* import korlibs.korge.ui.* import korlibs.korge.view.* import korlibs.math.* +import kotlinx.atomicfu.* import kotlin.math.* class MainPolyphonic : Scene() { @@ -55,11 +56,11 @@ class MainPolyphonic : Scene() { data class Note_t(val note: Int, val octave: Int, val duration: Int) data class ChannelState_t( - val currentNote: KorAtomicRef = KorAtomicRef(Note_t(0, 0, 0)), - val noteIndex: KorAtomicInt = KorAtomicInt(0), - val currentTime: KorAtomicInt = KorAtomicInt(0), - val currentsampleIndex: KorAtomicFloat = KorAtomicFloat(0f), - val currentsampleIncrement: KorAtomicFloat = KorAtomicFloat(0f) + val currentNote: AtomicRef = atomic(Note_t(0, 0, 0)), + val noteIndex: AtomicInt = atomic(0), + val currentTime: AtomicInt = atomic(0), + val currentsampleIndex: AtomicRef = atomic(0f), + val currentsampleIncrement: AtomicRef = atomic(0f) ) val channelStates = Array(2) { ChannelState_t() } @@ -408,3 +409,6 @@ class MainPolyphonic : Scene() { } } } + +private fun AtomicRef.addAndGetMod(delta: Float, modulo: Float): Float = updateAndGet { (it + delta) % modulo } +private fun AtomicRef.addAndGetMod(delta: Double, modulo: Double): Double = updateAndGet { (it + delta) % modulo } diff --git a/korge-sandbox/src/samples/MainShapes.kt b/korge-sandbox/src/samples/MainShapes.kt index 1aff30d0a9..94fa9a923b 100644 --- a/korge-sandbox/src/samples/MainShapes.kt +++ b/korge-sandbox/src/samples/MainShapes.kt @@ -2,11 +2,13 @@ package samples import korlibs.image.color.* import korlibs.io.async.* +import korlibs.io.async.launch import korlibs.korge.input.* import korlibs.korge.scene.* import korlibs.korge.view.* import korlibs.math.geom.* import korlibs.time.* +import kotlinx.coroutines.* class MainShapes : Scene() { override suspend fun SContainer.sceneMain() { diff --git a/korge-sandbox/src/samples/MainTextMetrics.kt b/korge-sandbox/src/samples/MainTextMetrics.kt index 6980b4caff..4ca53cbad4 100644 --- a/korge-sandbox/src/samples/MainTextMetrics.kt +++ b/korge-sandbox/src/samples/MainTextMetrics.kt @@ -6,6 +6,7 @@ import korlibs.image.color.* import korlibs.image.font.* import korlibs.image.text.* import korlibs.io.async.* +import korlibs.io.file.* import korlibs.io.file.std.* import korlibs.korge.input.* import korlibs.korge.scene.* diff --git a/korge-sandbox/src/samples/MainTweenPoint.kt b/korge-sandbox/src/samples/MainTweenPoint.kt index 20e3e823e1..bff3eafbcb 100644 --- a/korge-sandbox/src/samples/MainTweenPoint.kt +++ b/korge-sandbox/src/samples/MainTweenPoint.kt @@ -4,6 +4,7 @@ import korlibs.image.color.* import korlibs.image.format.* import korlibs.image.vector.* import korlibs.io.async.* +import korlibs.io.async.launch import korlibs.io.file.std.* import korlibs.korge.scene.* import korlibs.korge.tween.* @@ -13,6 +14,7 @@ import korlibs.math.geom.shape.* import korlibs.math.geom.vector.* import korlibs.math.interpolation.* import korlibs.time.* +import kotlinx.coroutines.* class MainTweenPoint : Scene() { override suspend fun SContainer.sceneMain() { diff --git a/korge-sandbox/src/samples/asteroids/Game.kt b/korge-sandbox/src/samples/asteroids/Game.kt index d8df74ed5c..ab72e3bf8c 100644 --- a/korge-sandbox/src/samples/asteroids/Game.kt +++ b/korge-sandbox/src/samples/asteroids/Game.kt @@ -11,6 +11,7 @@ import korlibs.korge.view.align.* import korlibs.math.geom.* import korlibs.math.random.* import korlibs.time.* +import kotlinx.coroutines.* import onCollision import kotlin.random.* diff --git a/korge-sandbox/src/samples/minesweeper/Process.kt b/korge-sandbox/src/samples/minesweeper/Process.kt index aab2d893f5..2b47877112 100644 --- a/korge-sandbox/src/samples/minesweeper/Process.kt +++ b/korge-sandbox/src/samples/minesweeper/Process.kt @@ -12,50 +12,49 @@ import korlibs.korge.view.* import korlibs.image.format.* import korlibs.io.async.* import korlibs.io.file.std.* -import korlibs.io.lang.Closeable import korlibs.io.lang.CancellableGroup import korlibs.math.geom.* import kotlinx.coroutines.* import kotlin.reflect.* abstract class Process(parent: Container) : Container() { - init { - parent.addChild(this) - } + init { + parent.addChild(this) + } - override val stage: Stage get() = super.stage!! + override val stage: Stage get() = super.stage!! val processRoot: SContainer get() = parent.findFirstAscendant { it.name == "process.root" } as SContainer val scene: ScaledScene get() = processRoot.getExtraTyped("scene")!! - val views: Views get() = stage.views - var fps: Double = 60.0 - - val key: KeyV get() = scene.key - val mouse: MouseV get() = scene.mouseV - val audio: AudioV get() = scene.audioV - - suspend fun frame() { - delayFrame() - } - - fun action(action: KSuspendFunction0) { - throw ChangeActionException(action) - } - - abstract suspend fun main() - - private val job: Job = scene.launchAsap { - var action = ::main - while (true) { - try { - action() - break - } catch (e: ChangeActionException) { - action = e.action - } - } - } - - init { + val views: Views get() = stage.views + var fps: Double = 60.0 + + val key: KeyV get() = scene.key + val mouse: MouseV get() = scene.mouseV + val audio: AudioV get() = scene.audioV + + suspend fun frame() { + delayFrame() + } + + fun action(action: KSuspendFunction0) { + throw ChangeActionException(action) + } + + abstract suspend fun main() + + private val job: Job = scene.launchAsap { + var action = ::main + while (true) { + try { + action() + break + } catch (e: ChangeActionException) { + action = e.action + } + } + } + + init { this.onAttachDetach( onAttach = { //println("added: $views") @@ -66,42 +65,42 @@ abstract class Process(parent: Container) : Container() { onDestroy() } ) - } + } - fun destroy() { - removeFromParent() - } + fun destroy() { + removeFromParent() + } - protected open fun onDestroy() { - } + protected open fun onDestroy() { + } - class ChangeActionException(val action: KSuspendFunction0) : Exception() + class ChangeActionException(val action: KSuspendFunction0) : Exception() - inline fun collision(): T? = views.stage.findCollision(this) + inline fun collision(): T? = views.stage.findCollision(this) } class KeyV(val views: ScaledScene) { - operator fun get(key: Key): Boolean = views.keysPressed[key] == true + operator fun get(key: Key): Boolean = views.keysPressed[key] == true } class MouseV(val scene: ScaledScene) { - val left: Boolean get() = pressing[0] - val right: Boolean get() = pressing[1] || pressing[2] + val left: Boolean get() = pressing[0] + val right: Boolean get() = pressing[1] || pressing[2] val pos: PointInt get() = (scene.sceneView.localMousePos(scene.views)).toInt() - val x: Int get() = pos.x - val y: Int get() = pos.y - val pressing = BooleanArray(8) - val pressed = BooleanArray(8) - val released = BooleanArray(8) - val _pressed = BooleanArray(8) - val _released = BooleanArray(8) + val x: Int get() = pos.x + val y: Int get() = pos.y + val pressing = BooleanArray(8) + val pressed = BooleanArray(8) + val released = BooleanArray(8) + val _pressed = BooleanArray(8) + val _released = BooleanArray(8) } class AudioV(val views: ScaledScene) { - fun play(sound: Sound, repeat: Int = 0) { - val times = (1 + repeat).playbackTimes - sound.play(views.coroutineContext, times) - } + fun play(sound: Sound, repeat: Int = 0) { + val times = (1 + repeat).playbackTimes + sound.play(views.coroutineContext, times) + } } val ScaledScene.keysPressed by Extra.Property { LinkedHashMap() } @@ -110,38 +109,40 @@ val ScaledScene.key by Extra.PropertyThis { KeyV(this) } val ScaledScene.mouseV by Extra.PropertyThis { MouseV(this) } val ScaledScene.audioV by Extra.PropertyThis { AudioV(this) } -fun ScaledScene.registerProcessSystem(): Closeable { +fun ScaledScene.registerProcessSystem(): AutoCloseable { val closeable = CancellableGroup() closeable += stage.onEvents(*MouseEvent.Type.ALL) { e -> - when (e.type) { - MouseEvent.Type.MOVE -> Unit - MouseEvent.Type.DRAG -> Unit - MouseEvent.Type.UP -> { - mouseV.pressing[e.button.id] = false - mouseV._released[e.button.id] = true - } - MouseEvent.Type.DOWN -> { - mouseV.pressing[e.button.id] = true - mouseV._pressed[e.button.id] = true - } - MouseEvent.Type.CLICK -> Unit - MouseEvent.Type.ENTER -> Unit - MouseEvent.Type.EXIT -> Unit - MouseEvent.Type.SCROLL -> Unit - } - } - // @TODO: Use onAfterRender + when (e.type) { + MouseEvent.Type.MOVE -> Unit + MouseEvent.Type.DRAG -> Unit + MouseEvent.Type.UP -> { + mouseV.pressing[e.button.id] = false + mouseV._released[e.button.id] = true + } + + MouseEvent.Type.DOWN -> { + mouseV.pressing[e.button.id] = true + mouseV._pressed[e.button.id] = true + } + + MouseEvent.Type.CLICK -> Unit + MouseEvent.Type.ENTER -> Unit + MouseEvent.Type.EXIT -> Unit + MouseEvent.Type.SCROLL -> Unit + } + } + // @TODO: Use onAfterRender closeable += views.onBeforeRender { - arraycopy(mouseV._released, 0, mouseV.released, 0, 8) - arraycopy(mouseV._pressed, 0, mouseV.pressed, 0, 8) + arraycopy(mouseV._released, 0, mouseV.released, 0, 8) + arraycopy(mouseV._pressed, 0, mouseV.pressed, 0, 8) - mouseV._released.fill(false) - mouseV._pressed.fill(false) - } + mouseV._released.fill(false) + mouseV._pressed.fill(false) + } closeable += stage.onEvents(*KeyEvent.Type.ALL) { e -> - keysPressed[e.key] = e.type == KeyEvent.Type.DOWN - } + keysPressed[e.key] = e.type == KeyEvent.Type.DOWN + } return closeable } diff --git a/korge/build.gradle.kts b/korge/build.gradle.kts index b2cb906c3a..ee7adc1729 100644 --- a/korge/build.gradle.kts +++ b/korge/build.gradle.kts @@ -15,6 +15,9 @@ dependencies { commonMainApi(libs.korlibs.image) commonMainApi(libs.korlibs.inject) commonMainApi(libs.korlibs.template) + commonMainApi(libs.korlibs.time) + commonMainApi(libs.kotlinx.atomicfu) + commonMainApi(libs.kotlinx.coroutines.core) //commonTestApi(project(":korge-test")) jvmMainApi("org.jetbrains.kotlin:kotlin-reflect") jvmMainImplementation(libs.jackson.databind) diff --git a/korge/src/korlibs/datastructure/_Datastructure_event.kt b/korge/src/korlibs/datastructure/_Datastructure_event.kt index 140641269a..83f7b6a618 100644 --- a/korge/src/korlibs/datastructure/_Datastructure_event.kt +++ b/korge/src/korlibs/datastructure/_Datastructure_event.kt @@ -2,25 +2,32 @@ package korlibs.datastructure.event +import korlibs.concurrent.lock.* +import korlibs.concurrent.thread.* import korlibs.datastructure.* -import korlibs.datastructure.closeable.* import korlibs.datastructure.lock.* +import korlibs.datastructure.lock.Lock import korlibs.datastructure.pauseable.* import korlibs.datastructure.thread.* +import korlibs.datastructure.thread.NativeThread +import korlibs.datastructure.thread.nativeThread import korlibs.logger.* import korlibs.time.* +import kotlinx.atomicfu.locks.* import kotlin.time.* expect fun createPlatformEventLoop(precise: Boolean = true): SyncEventLoop -interface EventLoop : Pauseable, Closeable { +interface EventLoop : Pauseable, AutoCloseable { companion object + fun setImmediate(task: () -> Unit) - fun setTimeout(time: TimeSpan, task: () -> Unit): Closeable - fun setInterval(time: TimeSpan, task: () -> Unit): Closeable - fun setIntervalFrame(task: () -> Unit): Closeable = setInterval(60.hz.timeSpan, task) + fun setTimeout(time: Duration, task: () -> Unit): AutoCloseable + fun setInterval(time: Duration, task: () -> Unit): AutoCloseable + fun setIntervalFrame(task: () -> Unit): AutoCloseable = setInterval(60.hz.timeSpan, task) } -fun EventLoop.setInterval(time: Frequency, task: () -> Unit): Closeable = setInterval(time.timeSpan, task) + +fun EventLoop.setInterval(time: Frequency, task: () -> Unit): AutoCloseable = setInterval(time.timeSpan, task) abstract class BaseEventLoop : EventLoop, Pauseable { val runLock = Lock() @@ -35,14 +42,17 @@ open class SyncEventLoop( ) : BaseEventLoop(), Pauseable { private val pauseable = SyncPauseable() override var paused: Boolean by pauseable::paused - private val lock = NonRecursiveLock() + private val lock = korlibs.concurrent.lock.Lock() private var running = true - protected class TimedTask(val eventLoop: SyncEventLoop, var now: Duration, val time: Duration, var interval: Boolean, val callback: () -> Unit) : Comparable, Closeable { + + protected class TimedTask(val eventLoop: SyncEventLoop, var now: Duration, val time: Duration, var interval: Boolean, val callback: () -> Unit) : + Comparable, AutoCloseable { var timeMark: Duration get() = now + time set(value) { now = value - time } + override fun compareTo(other: TimedTask): Int = this.timeMark.compareTo(other.timeMark) override fun close() { //println("CLOSE") @@ -50,6 +60,7 @@ open class SyncEventLoop( eventLoop.timedTasks.remove(this) } } + private val startTime = TimeSource.Monotonic.markNow() var nowProvider: () -> Duration = { startTime.elapsedNow() } @@ -73,15 +84,15 @@ open class SyncEventLoop( } } - override fun setTimeout(time: TimeSpan, task: () -> Unit): Closeable { + override fun setTimeout(time: Duration, task: () -> Unit): AutoCloseable { return _queueAfter(time, interval = false, task = task) } - override fun setInterval(time: TimeSpan, task: () -> Unit): Closeable { + override fun setInterval(time: Duration, task: () -> Unit): AutoCloseable { return _queueAfter(time, interval = true, task = task) } - private fun _queueAfter(time: TimeSpan, interval: Boolean, task: () -> Unit): Closeable { + private fun _queueAfter(time: Duration, interval: Boolean, task: () -> Unit): AutoCloseable { //println("_queueAfter: time=$time, interval=$interval, task=$task") return lock { val task = TimedTask(this, now, time, interval, task) @@ -113,7 +124,7 @@ open class SyncEventLoop( return now >= timedTasks.head.timeMark } - protected fun wait(waitTime: TimeSpan) { + protected fun wait(waitTime: Duration) { if (immediateRun) return lock.wait(waitTime, precise) } @@ -224,6 +235,7 @@ open class SyncEventLoop( runTasksForever { thread?.threadSuggestRunning == true } } } + open fun stop(): Unit { thread?.threadSuggestRunning = false thread = null diff --git a/korge/src/korlibs/datastructure/closeable/Closeable.kt b/korge/src/korlibs/datastructure/closeable/Closeable.kt new file mode 100644 index 0000000000..8217357f06 --- /dev/null +++ b/korge/src/korlibs/datastructure/closeable/Closeable.kt @@ -0,0 +1,4 @@ +package korlibs.datastructure.closeable + +@Deprecated("", replaceWith = ReplaceWith("kotlin.AutoCloseable")) +typealias Closeable = AutoCloseable diff --git a/korge/src/korlibs/datastructure/lock/LockAlias.kt b/korge/src/korlibs/datastructure/lock/LockAlias.kt new file mode 100644 index 0000000000..406276a497 --- /dev/null +++ b/korge/src/korlibs/datastructure/lock/LockAlias.kt @@ -0,0 +1,7 @@ +package korlibs.datastructure.lock + +@Deprecated("") +typealias Lock = korlibs.concurrent.lock.Lock + +@Deprecated("") +typealias NonRecursiveLock = korlibs.concurrent.lock.NonRecursiveLock diff --git a/korge/src/korlibs/datastructure/thread/ThreadAlias.kt b/korge/src/korlibs/datastructure/thread/ThreadAlias.kt new file mode 100644 index 0000000000..ddb5b6613e --- /dev/null +++ b/korge/src/korlibs/datastructure/thread/ThreadAlias.kt @@ -0,0 +1,13 @@ +package korlibs.datastructure.thread + +@Deprecated("", ReplaceWith("korlibs.concurrent.thread.NativeThread")) +typealias NativeThread = korlibs.concurrent.thread.NativeThread + +@Deprecated("", ReplaceWith("korlibs.concurrent.thread.nativeThread(start, isDaemon, name, priority, block)", "korlibs")) +public fun nativeThread( + start: Boolean = true, + isDaemon: Boolean = false, + name: String? = null, + priority: Int = -1, + block: (NativeThread) -> Unit +): NativeThread = korlibs.concurrent.thread.nativeThread(start, isDaemon, name, priority, block) diff --git a/korge/src/korlibs/event/EventListener.kt b/korge/src/korlibs/event/EventListener.kt index c4667410d4..317b6a4e7a 100644 --- a/korge/src/korlibs/event/EventListener.kt +++ b/korge/src/korlibs/event/EventListener.kt @@ -12,9 +12,9 @@ interface EventListener { /** * Registers a [handler] block to be executed when an event of [type] is [dispatch]ed */ - fun onEvent(type: EventType, handler: (T) -> Unit): Closeable + fun onEvent(type: EventType, handler: (T) -> Unit): AutoCloseable - fun onEvents(vararg etypes: EventType, handler: (T) -> Unit): Closeable { + fun onEvents(vararg etypes: EventType, handler: (T) -> Unit): AutoCloseable { if (etypes.isEmpty()) error("Must have at least one event type") val closeable = CancellableGroup() etypes.fastForEach { closeable += onEvent(it, handler) } @@ -27,12 +27,9 @@ interface EventListener { * in this object and its children. */ fun dispatch(type: EventType, event: T, result: EventResult?, up: Boolean, down: Boolean): Boolean - fun dispatch(type: EventType, event: T, result: EventResult? = null): Boolean - = dispatch(type, event, result, up = true, down = true) - fun dispatchDown(type: EventType, event: T, result: EventResult? = null): Boolean - = dispatch(type, event, result, up = false, down = true) - fun dispatchUp(type: EventType, event: T, result: EventResult? = null): Boolean - = dispatch(type, event, result, up = true, down = false) + fun dispatch(type: EventType, event: T, result: EventResult? = null): Boolean = dispatch(type, event, result, up = true, down = true) + fun dispatchDown(type: EventType, event: T, result: EventResult? = null): Boolean = dispatch(type, event, result, up = false, down = true) + fun dispatchUp(type: EventType, event: T, result: EventResult? = null): Boolean = dispatch(type, event, result, up = true, down = false) fun dispatch(event: T): Boolean = dispatch(event.fastCastTo>().type, event) diff --git a/korge/src/korlibs/event/gamepad/X11JoyGameControllerCommon.kt b/korge/src/korlibs/event/gamepad/X11JoyGameControllerCommon.kt index 8615e15bed..964b9e9d3e 100644 --- a/korge/src/korlibs/event/gamepad/X11JoyGameControllerCommon.kt +++ b/korge/src/korlibs/event/gamepad/X11JoyGameControllerCommon.kt @@ -2,6 +2,7 @@ package korlibs.event.gamepad import korlibs.datastructure.* import korlibs.datastructure.iterators.* +import korlibs.datastructure.thread.* import korlibs.event.* import korlibs.io.concurrent.* import korlibs.io.concurrent.atomic.* @@ -12,13 +13,15 @@ import korlibs.math.* import korlibs.memory.* import korlibs.platform.* import korlibs.time.* +import kotlinx.atomicfu.* import kotlinx.coroutines.* import kotlin.coroutines.* +import kotlin.time.* /** * */ -internal class LinuxJoyEventAdapter @OptIn(SyncIOAPI::class) constructor(val syncIO: SyncIO = SyncIO) : Closeable { +internal class LinuxJoyEventAdapter @OptIn(SyncIOAPI::class) constructor(val syncIO: SyncIO = SyncIO) : AutoCloseable { companion object { const val JS_EVENT_BUTTON = 0x01 /* button pressed/released */ const val JS_EVENT_AXIS = 0x02 /* joystick moved */ @@ -41,7 +44,7 @@ internal class LinuxJoyEventAdapter @OptIn(SyncIOAPI::class) constructor(val syn .sortedBy { it.id } } - class RunEvery(val time: TimeSpan) { + class RunEvery(val time: Duration) { var lastRun = DateTime.EPOCH operator fun invoke(block: () -> Unit) { val now = DateTime.now() @@ -90,7 +93,7 @@ internal class LinuxJoyEventAdapter @OptIn(SyncIOAPI::class) constructor(val syn readers.clear() } - internal class X11JoystickReader(val info: DeviceInfo, val platformSyncIO: SyncIO) : Closeable { + internal class X11JoystickReader(val info: DeviceInfo, val platformSyncIO: SyncIO) : AutoCloseable { val index: Int = info.id private val buttonsPressure = FloatArray(GameButton.MAX) @@ -105,7 +108,7 @@ internal class LinuxJoyEventAdapter @OptIn(SyncIOAPI::class) constructor(val syn } private var running = true - val readCount = KorAtomicInt(0) + val readCount = atomic(0) val once = CompletableDeferred() // @TODO: This Hangs WASM @@ -191,14 +194,14 @@ internal class LinuxJoyEventAdapter @OptIn(SyncIOAPI::class) constructor(val syn } //println("$time, $type, $number, $value: ${buttons.toStringUnsigned(2)}, ${axes.slice(0 until maxAxes).toList()}") } else { - Thread_sleep(10L) + NativeThread.sleep(10.fastMilliseconds) } readCount.incrementAndGet() once.complete(Unit) } } catch (e: Throwable) { e.printStackTrace() - Thread_sleep(100L) + NativeThread.sleep(100.fastMilliseconds) } } } diff --git a/korge/src/korlibs/graphics/AGObjects.kt b/korge/src/korlibs/graphics/AGObjects.kt index f38ed9d458..02e269718b 100644 --- a/korge/src/korlibs/graphics/AGObjects.kt +++ b/korge/src/korlibs/graphics/AGObjects.kt @@ -16,7 +16,7 @@ internal interface AGNativeObject { fun markToDelete() } -open class AGObject : Closeable { +open class AGObject : AutoCloseable { internal var _native: AGNativeObject? = null internal var _cachedContextVersion: Int = -1 internal var _cachedVersion: Int = -2 @@ -144,7 +144,7 @@ inline class AGTextureUnitInfo private constructor(val data: Int) { class AGTexture( val targetKind: AGTextureTargetKind = AGTextureTargetKind.TEXTURE_2D -) : AGObject(), Closeable { +) : AGObject(), AutoCloseable { private val logger = Logger("AGTexture") var isFbo: Boolean = false var requestMipmaps: Boolean = false @@ -207,7 +207,7 @@ open class AGFrameBufferBase(val isMain: Boolean) : AGObject() { override fun toString(): String = "AGFrameBufferBase(isMain=$isMain)" } -open class AGFrameBuffer(val base: AGFrameBufferBase, val id: Int = -1) : Closeable { +open class AGFrameBuffer(val base: AGFrameBufferBase, val id: Int = -1) : AutoCloseable { constructor(isMain: Boolean = false, id: Int = -1) : this(AGFrameBufferBase(isMain), id) val isTexture: Boolean get() = base.isTexture val isMain: Boolean get() = base.isMain diff --git a/korge/src/korlibs/graphics/gl/AGOpengl.kt b/korge/src/korlibs/graphics/gl/AGOpengl.kt index 8a6273ba90..051a9a531f 100644 --- a/korge/src/korlibs/graphics/gl/AGOpengl.kt +++ b/korge/src/korlibs/graphics/gl/AGOpengl.kt @@ -2,6 +2,7 @@ package korlibs.graphics.gl import korlibs.datastructure.* import korlibs.datastructure.iterators.* +import korlibs.datastructure.thread.* import korlibs.encoding.* import korlibs.graphics.* import korlibs.graphics.shader.* @@ -329,15 +330,15 @@ class AGOpengl(val gl: KmlGl, var context: KmlGlContext? = null) : AG() { fun listStart() { if (renderThreadId == -1L) { - renderThreadId = currentThreadId - renderThreadName = currentThreadName - if (currentThreadName?.contains("DefaultDispatcher-worker") == true) { + renderThreadId = NativeThread.currentThreadId + renderThreadName = NativeThread.currentThreadName + if (NativeThread.currentThreadName?.contains("DefaultDispatcher-worker") == true) { println("DefaultDispatcher-worker!") printStackTrace() } } - if (renderThreadId != currentThreadId) { - println("AGQueueProcessorOpenGL.listStart: CALLED FROM DIFFERENT THREAD! ${renderThreadName}:${renderThreadId} != $currentThreadName:$currentThreadId") + if (renderThreadId != NativeThread.currentThreadId) { + println("AGQueueProcessorOpenGL.listStart: CALLED FROM DIFFERENT THREAD! ${renderThreadName}:${renderThreadId} != ${NativeThread.currentThreadName}:${NativeThread.currentThreadId}") printStackTrace() } } diff --git a/korge/src/korlibs/graphics/shader/shaders.kt b/korge/src/korlibs/graphics/shader/shaders.kt index f1c89c99c4..31d1a42770 100644 --- a/korge/src/korlibs/graphics/shader/shaders.kt +++ b/korge/src/korlibs/graphics/shader/shaders.kt @@ -378,7 +378,7 @@ inline fun Program.appendingFragment(extraName: String, block: Program.Builder.( data class UniformInProgram(val uniform: Uniform, val index: Int) -data class Program(val vertex: VertexShader, val fragment: FragmentShader, val name: String = "program-${vertex.name}-${fragment.name}") : Closeable { +data class Program(val vertex: VertexShader, val fragment: FragmentShader, val name: String = "program-${vertex.name}-${fragment.name}") : AutoCloseable { val uniforms = (vertex.uniforms + fragment.uniforms) val typedUniforms = (vertex.typedUniforms + fragment.typedUniforms) val uniformBlocks = typedUniforms.map { it.block }.distinct() diff --git a/korge/src/korlibs/io/annotations/Keep.kt b/korge/src/korlibs/io/annotations/Keep.kt new file mode 100644 index 0000000000..911ab34f05 --- /dev/null +++ b/korge/src/korlibs/io/annotations/Keep.kt @@ -0,0 +1,4 @@ +package korlibs.io.annotations + +@Deprecated("", ReplaceWith("korlibs.annotations.Keep")) +typealias Keep = korlibs.annotations.Keep diff --git a/korge/src/korlibs/io/annotations/KorInternal.kt b/korge/src/korlibs/io/annotations/KorInternal.kt new file mode 100644 index 0000000000..4b07fa7b5e --- /dev/null +++ b/korge/src/korlibs/io/annotations/KorInternal.kt @@ -0,0 +1,3 @@ +package korlibs.io.annotations + +annotation class KorInternal diff --git a/korge/src/korlibs/io/async/AsyncAlias.kt b/korge/src/korlibs/io/async/AsyncAlias.kt new file mode 100644 index 0000000000..e04178d51e --- /dev/null +++ b/korge/src/korlibs/io/async/AsyncAlias.kt @@ -0,0 +1,3 @@ +package korlibs.io.async + +//typealias launch = korlibs.concurrent. diff --git a/korge/src/korlibs/io/lang/Disposable.kt b/korge/src/korlibs/io/lang/Disposable.kt new file mode 100644 index 0000000000..356c7c9891 --- /dev/null +++ b/korge/src/korlibs/io/lang/Disposable.kt @@ -0,0 +1,8 @@ +package korlibs.io.lang + +import kotlinx.coroutines.* + +@Deprecated("", replaceWith = ReplaceWith("kotlinx.coroutines.DisposableHandle")) +typealias Disposable = DisposableHandle + +fun AutoCloseable.toDisposable(): DisposableHandle = DisposableHandle { this.close() } diff --git a/korge/src/korlibs/kgl/KmlGlContext.kt b/korge/src/korlibs/kgl/KmlGlContext.kt index e7a9659c51..e22e044a50 100644 --- a/korge/src/korlibs/kgl/KmlGlContext.kt +++ b/korge/src/korlibs/kgl/KmlGlContext.kt @@ -15,7 +15,7 @@ inline fun KmlGlContextDefaultTemp(block: (KmlGl) -> Unit) { } } -abstract class KmlGlContext(val window: Any?, val gl: KmlGl, val parent: KmlGlContext? = null) : Closeable { +abstract class KmlGlContext(val window: Any?, val gl: KmlGl, val parent: KmlGlContext? = null) : AutoCloseable { open fun set() { } open fun unset() { diff --git a/korge/src/korlibs/korge/Korge.kt b/korge/src/korlibs/korge/Korge.kt index dcc2c82c83..b6f3c36e7d 100644 --- a/korge/src/korlibs/korge/Korge.kt +++ b/korge/src/korlibs/korge/Korge.kt @@ -31,6 +31,7 @@ import korlibs.time.* import kotlinx.coroutines.* import kotlin.coroutines.* import kotlin.reflect.* +import kotlin.time.* typealias KorgeConfig = Korge @@ -284,7 +285,7 @@ object KorgeRunner { eventDispatcher: EventListener, clearEachFrame: Boolean = true, bgcolor: RGBA = Colors.TRANSPARENT, - fixedSizeStep: TimeSpan = TimeSpan.NIL, + fixedSizeStep: Duration = TimeSpan.NIL, forceRenderEveryFrame: Boolean = true, configInjector: Injector.() -> Unit = {}, ): CompletableDeferred { diff --git a/korge/src/korlibs/korge/animate/Animator.kt b/korge/src/korlibs/korge/animate/Animator.kt index 81b0fe62c4..13c5e523ba 100644 --- a/korge/src/korlibs/korge/animate/Animator.kt +++ b/korge/src/korlibs/korge/animate/Animator.kt @@ -14,6 +14,7 @@ import korlibs.time.* import kotlinx.coroutines.* import kotlin.math.* import kotlin.reflect.* +import kotlin.time.* @DslMarker @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) @@ -24,7 +25,7 @@ val View.simpleAnimator: Animator by Extra.PropertyThis { } fun View.animator( - defaultTime: TimeSpan = Animator.DEFAULT_TIME, + defaultTime: Duration = Animator.DEFAULT_TIME, defaultSpeed: Double = Animator.DEFAULT_SPEED, defaultEasing: Easing = Animator.DEFAULT_EASING, parallel: Boolean = false, @@ -34,7 +35,7 @@ fun View.animator( ): Animator = Animator(this, defaultTime, defaultSpeed, defaultEasing, parallel, looped, parent = null, startImmediately = startImmediately, level = 0).apply(block) suspend fun View.animate( - defaultTime: TimeSpan = Animator.DEFAULT_TIME, + defaultTime: Duration = Animator.DEFAULT_TIME, defaultSpeed: Double = Animator.DEFAULT_SPEED, defaultEasing: Easing = Animator.DEFAULT_EASING, parallel: Boolean = false, @@ -46,7 +47,7 @@ suspend fun View.animate( open class Animator @PublishedApi internal constructor( @PublishedApi internal val root: View, - @PublishedApi internal val defaultTime: TimeSpan, + @PublishedApi internal val defaultTime: Duration, @PublishedApi internal val defaultSpeed: Double, @PublishedApi internal val defaultEasing: Easing, private val parallel: Boolean = false, @@ -174,26 +175,50 @@ open class Animator @PublishedApi internal constructor( } inline fun parallel( - time: TimeSpan = this.defaultTime, speed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing, looped: Boolean = false, startImmediately: Boolean = this.startImmediately, + time: Duration = this.defaultTime, + speed: Double = this.defaultSpeed, + easing: Easing = this.defaultEasing, + looped: Boolean = false, + startImmediately: Boolean = this.startImmediately, callback: @AnimatorDslMarker Animator.() -> Unit ): Animator = Animator(root, time, speed, easing, true, looped, level = level + 1, parent = this, startImmediately = startImmediately).also { callback(it) }.also { addNode(it.rootAnimationNode) } inline fun sequence( - defaultTime: TimeSpan = this.defaultTime, defaultSpeed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing, looped: Boolean = false, startImmediately: Boolean = this.startImmediately, + defaultTime: Duration = this.defaultTime, + defaultSpeed: Double = this.defaultSpeed, + easing: Easing = this.defaultEasing, + looped: Boolean = false, + startImmediately: Boolean = this.startImmediately, callback: @AnimatorDslMarker Animator.() -> Unit ): Animator = Animator(root, defaultTime, defaultSpeed, easing, false, looped, level = level + 1, parent = this, startImmediately = startImmediately).also { callback(it) }.also { addNode(it.rootAnimationNode) } fun parallelLazy( - time: TimeSpan = this.defaultTime, speed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing, looped: Boolean = false, startImmediately: Boolean = this.startImmediately, + time: Duration = this.defaultTime, + speed: Double = this.defaultSpeed, + easing: Easing = this.defaultEasing, + looped: Boolean = false, + startImmediately: Boolean = this.startImmediately, init: @AnimatorDslMarker Animator.() -> Unit ): Animator = Animator(root, time, speed, easing, true, looped, lazyInit = init, level = level + 1, parent = this, startImmediately = startImmediately).also { addNode(it.rootAnimationNode) } fun sequenceLazy( - time: TimeSpan = this.defaultTime, speed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing, looped: Boolean = false, startImmediately: Boolean = this.startImmediately, + time: Duration = this.defaultTime, + speed: Double = this.defaultSpeed, + easing: Easing = this.defaultEasing, + looped: Boolean = false, + startImmediately: Boolean = this.startImmediately, init: @AnimatorDslMarker Animator.() -> Unit ): Animator = Animator(root, time, speed, easing, false, looped, lazyInit = init, level = level + 1, parent = this, startImmediately = startImmediately).also { addNode(it.rootAnimationNode) } - @PublishedApi internal fun __tween(vararg vs: V2<*>, lazyVs: Array V2<*>>? = null, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing, name: String?, replace: Boolean = true) { + @PublishedApi internal fun __tween( + vararg vs: V2<*>, + lazyVs: Array V2<*>>? = null, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing, + name: String?, + replace: Boolean = true + ) { //println("__tween=time=$time") if (replace && parallel) { removeProps(vs.map { it.key }.toSet()) @@ -201,7 +226,13 @@ open class Animator @PublishedApi internal constructor( addNode(TweenNode(*vs, lazyVs = lazyVs, time = time, lazyTime = lazyTime, easing = easing, name = name)) } - @PublishedApi internal fun __tween(vararg vs: () -> V2<*>, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing, name: String?) { + @PublishedApi internal fun __tween( + vararg vs: () -> V2<*>, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing, + name: String? + ) { addNode(TweenNode(lazyVs = vs, time = time, lazyTime = lazyTime, easing = easing, name = name)) } @@ -226,7 +257,7 @@ open class Animator @PublishedApi internal constructor( private val toRemove = fastArrayListOf() - override fun update(dt: TimeSpan): TimeSpan { + override fun update(dt: Duration): Duration { var dt = if (speed != 1.0) dt * speed else dt //println("UPDATE!!: dt=$dt") @@ -296,22 +327,22 @@ open class Animator @PublishedApi internal constructor( class TweenNode( vararg val vs: V2<*>, val lazyVs: Array V2<*>>? = null, - val time: TimeSpan = 1000.milliseconds, - val lazyTime: (() -> TimeSpan)? = null, + val time: Duration = 1000.milliseconds, + val lazyTime: (() -> Duration)? = null, val easing: Easing, val name: String? = null ) : NewAnimatorNode { val computedVs by lazy { (lazyVs?.map { it() } ?: vs.toList()).toMutableList() } - private val totalTime: TimeSpan by lazy { lazyTime?.invoke() ?: time } + private val totalTime: Duration by lazy { lazyTime?.invoke() ?: time } override fun toString(): String = "TweenNode(totalTime=$totalTime, name=$name, ${computedVs.toList()})" - private var currentTime: TimeSpan = TimeSpan.ZERO + private var currentTime: Duration = TimeSpan.ZERO override fun reset() { currentTime = TimeSpan.ZERO } - override fun update(dt: TimeSpan): TimeSpan { + override fun update(dt: Duration): Duration { if (currentTime == TimeSpan.ZERO) { computedVs.fastForEach { it.init() @@ -346,10 +377,12 @@ open class Animator @PublishedApi internal constructor( override fun reset() { executed = false } - override fun update(dt: TimeSpan): TimeSpan { + + override fun update(dt: Duration): Duration { complete() return dt } + override fun complete() { if (executed) return executed = true @@ -361,21 +394,22 @@ interface NewAnimatorNode { //fun reset() //val totalTime: TimeSpan fun reset() + /** Sets this node to the specified [dt] (Delta Time), returns the exceeded time of completion, 0 if completed, less than zero if not completed. */ - fun update(dt: TimeSpan): TimeSpan + fun update(dt: Duration): Duration fun complete() } //////////////////////// -fun Animator.tween(vararg vs: V2<*>, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing, name: String? = null, replace: Boolean = true): Unit = __tween(*vs, time = time, easing = easing, name = name, replace = replace) -fun Animator.tweenLazyTime(vararg vs: V2<*>, time: () -> TimeSpan = { this.defaultTime }, easing: Easing = this.defaultEasing, name: String? = null, replace: Boolean = true) = __tween(*vs, lazyTime = time, easing = easing, name = name, replace = replace) +fun Animator.tween(vararg vs: V2<*>, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing, name: String? = null, replace: Boolean = true): Unit = __tween(*vs, time = time, easing = easing, name = name, replace = replace) +fun Animator.tweenLazyTime(vararg vs: V2<*>, time: () -> Duration = { this.defaultTime }, easing: Easing = this.defaultEasing, name: String? = null, replace: Boolean = true) = __tween(*vs, lazyTime = time, easing = easing, name = name, replace = replace) -fun Animator.tweenLazy(vararg vs: () -> V2<*>, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing, name: String? = null) = __tween(*vs, time = time, easing = easing, name = name) -fun Animator.tweenLazyLazyTime(vararg vs: () -> V2<*>, time: () -> TimeSpan = { this.defaultTime }, easing: Easing = this.defaultEasing, name: String? = null) = __tween(*vs, lazyTime = time, easing = easing, name = name) +fun Animator.tweenLazy(vararg vs: () -> V2<*>, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing, name: String? = null) = __tween(*vs, time = time, easing = easing, name = name) +fun Animator.tweenLazyLazyTime(vararg vs: () -> V2<*>, time: () -> Duration = { this.defaultTime }, easing: Easing = this.defaultEasing, name: String? = null) = __tween(*vs, lazyTime = time, easing = easing, name = name) -fun Animator.wait(time: TimeSpan = this.defaultTime) = __tween(time = time, name = "wait") -fun Animator.waitLazy(time: () -> TimeSpan) = __tween(lazyTime = time, name = "wait") +fun Animator.wait(time: Duration = this.defaultTime) = __tween(time = time, name = "wait") +fun Animator.waitLazy(time: () -> Duration) = __tween(lazyTime = time, name = "wait") fun Animator.block(name: String? = null, callback: () -> Unit) { addNode(Animator.BlockNode(name, callback)) @@ -387,44 +421,77 @@ fun Animator.removeFromParent(view: View) { //////////////////////// -fun Animator.scaleBy(view: View, scaleX: Double, scaleY: Double = scaleX, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::scaleX.incr(scaleX), view::scaleY.incr(scaleY), time = time, easing = easing, name = "scaleBy") -fun Animator.rotateBy(view: View, rotation: Angle, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::rotation.incr(rotation), time = time, easing = easing, name = "rotateBy") -fun Animator.moveBy(view: View, x: Double = 0.0, y: Double = 0.0, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::x.incr(x), view::y.incr(y), time = time, easing = easing, name = "moveBy") +fun Animator.scaleBy(view: View, scaleX: Double, scaleY: Double = scaleX, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::scaleX.incr(scaleX), view::scaleY.incr(scaleY), time = time, easing = easing, name = "scaleBy") +fun Animator.rotateBy(view: View, rotation: Angle, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::rotation.incr(rotation), time = time, easing = easing, name = "rotateBy") +fun Animator.moveBy(view: View, x: Double = 0.0, y: Double = 0.0, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::x.incr(x), view::y.incr(y), time = time, easing = easing, name = "moveBy") fun Animator.moveByWithSpeed(view: View, x: Double = 0.0, y: Double = 0.0, speed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing) = __tween(view::x.incr(x), view::y.incr(y), lazyTime = { (hypot(x, y) / speed.toDouble()).seconds }, easing = easing, name = "moveByWithSpeed") -fun Animator.moveBy(view: View, x: Number = 0.0, y: Number = 0.0, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = moveBy(view, x.toDouble(), y.toDouble(), time, easing) +fun Animator.moveBy(view: View, x: Number = 0.0, y: Number = 0.0, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = moveBy(view, x.toDouble(), y.toDouble(), time, easing) fun Animator.moveByWithSpeed(view: View, x: Number = 0.0, y: Number = 0.0, speed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing) = moveByWithSpeed(view, x.toDouble(), y.toDouble(), speed, easing) -fun Animator.scaleTo(view: View, scaleX: () -> Number, scaleY: () -> Number = scaleX, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing) = __tween({ view::scaleX[scaleX()] }, { view::scaleY[scaleY()] }, time = time, lazyTime = lazyTime, easing = easing, name = "scaleTo") -fun Animator.scaleTo(view: View, scaleX: Double, scaleY: Double = scaleX, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::scaleX[scaleX], view::scaleY[scaleY], time = time, easing = easing, name = "scaleTo") -fun Animator.scaleTo(view: View, scaleX: Float, scaleY: Float = scaleX, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = scaleTo(view, scaleX.toDouble(), scaleY.toDouble(), time, easing) -fun Animator.scaleTo(view: View, scaleX: Int, scaleY: Int = scaleX, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = scaleTo(view, scaleX.toDouble(), scaleY.toDouble(), time, easing) - -fun Animator.moveTo(view: View, x: () -> Number = { view.x }, y: () -> Number = { view.y }, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing) = __tween({ view::x[x()] }, { view::y[y()] }, time = time, lazyTime = lazyTime, easing = easing, name = "moveTo") -fun Animator.moveTo(view: View, x: Double, y: Double, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::x[x], view::y[y], time = time, easing = easing, name = "moveTo") -fun Animator.moveTo(view: View, x: Float, y: Float, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = moveTo(view, x.toDouble(), y.toDouble(), time, easing) -fun Animator.moveTo(view: View, x: Int, y: Int, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = moveTo(view, x.toDouble(), y.toDouble(), time, easing) +fun Animator.scaleTo( + view: View, + scaleX: () -> Number, + scaleY: () -> Number = scaleX, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing +) = __tween({ view::scaleX[scaleX()] }, { view::scaleY[scaleY()] }, time = time, lazyTime = lazyTime, easing = easing, name = "scaleTo") +fun Animator.scaleTo(view: View, scaleX: Double, scaleY: Double = scaleX, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::scaleX[scaleX], view::scaleY[scaleY], time = time, easing = easing, name = "scaleTo") +fun Animator.scaleTo(view: View, scaleX: Float, scaleY: Float = scaleX, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = scaleTo(view, scaleX.toDouble(), scaleY.toDouble(), time, easing) +fun Animator.scaleTo(view: View, scaleX: Int, scaleY: Int = scaleX, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = scaleTo(view, scaleX.toDouble(), scaleY.toDouble(), time, easing) + +fun Animator.moveTo( + view: View, + x: () -> Number = { view.x }, + y: () -> Number = { view.y }, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing +) = __tween({ view::x[x()] }, { view::y[y()] }, time = time, lazyTime = lazyTime, easing = easing, name = "moveTo") +fun Animator.moveTo(view: View, x: Double, y: Double, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::x[x], view::y[y], time = time, easing = easing, name = "moveTo") +fun Animator.moveTo(view: View, x: Float, y: Float, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = moveTo(view, x.toDouble(), y.toDouble(), time, easing) +fun Animator.moveTo(view: View, x: Int, y: Int, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = moveTo(view, x.toDouble(), y.toDouble(), time, easing) fun Animator.moveToWithSpeed(view: View, x: () -> Number = { view.x }, y: () -> Number = { view.y }, speed: () -> Number = { this.defaultSpeed }, easing: Easing = this.defaultEasing) = __tween({ view::x[x()] }, { view::y[y()] }, lazyTime = { (hypot(view.x - x().toDouble(), view.y - y().toDouble()) / speed().toDouble()).seconds }, easing = easing, name = "moveToWithSpeed") fun Animator.moveToWithSpeed(view: View, x: Double, y: Double, speed: Double = this.defaultSpeed, easing: Easing = this.defaultEasing) = __tween(view::x[x], view::y[y], lazyTime = { (hypot(view.x - x, view.y - y) / speed.toDouble()).seconds }, easing = easing, name = "moveToWithSpeed") fun Animator.moveToWithSpeed(view: View, x: Float, y: Float, speed: Number = this.defaultSpeed, easing: Easing = this.defaultEasing) = moveToWithSpeed(view, x.toDouble(), y.toDouble(), speed.toDouble(), easing) fun Animator.moveToWithSpeed(view: View, x: Int, y: Int, speed: Number = this.defaultSpeed, easing: Easing = this.defaultEasing) = moveToWithSpeed(view, x.toDouble(), y.toDouble(), speed.toDouble(), easing) -fun Animator.moveInPath(view: View, path: VectorPath, includeLastPoint: Boolean = true, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing) = __tween({ view::pos.get(path, includeLastPoint = includeLastPoint) }, time = time, lazyTime = lazyTime, easing = easing, name = "moveInPath") -fun Animator.moveInPath(view: View, points: PointList, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing) = __tween({ view::pos[points] }, time = time, lazyTime = lazyTime, easing = easing, name = "moveInPath") +fun Animator.moveInPath( + view: View, + path: VectorPath, + includeLastPoint: Boolean = true, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing +) = __tween({ view::pos.get(path, includeLastPoint = includeLastPoint) }, time = time, lazyTime = lazyTime, easing = easing, name = "moveInPath") +fun Animator.moveInPath( + view: View, + points: PointList, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing +) = __tween({ view::pos[points] }, time = time, lazyTime = lazyTime, easing = easing, name = "moveInPath") fun Animator.moveInPathWithSpeed(view: View, path: VectorPath, includeLastPoint: Boolean = true, speed: () -> Number = { this.defaultSpeed }, easing: Easing = this.defaultEasing) = __tween({ view::pos.get(path, includeLastPoint = includeLastPoint) }, lazyTime = { (path.length / speed().toDouble()).seconds }, easing = easing, name = "moveInPathWithSpeed") fun Animator.moveInPathWithSpeed(view: View, points: PointList, speed: () -> Number = { this.defaultSpeed }, easing: Easing = this.defaultEasing) = __tween({ view::pos[points] }, lazyTime = { (points.length / speed().toDouble()).seconds }, easing = easing, name = "moveInPathWithSpeed") -fun Animator.alpha(view: View, alpha: Float, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::alphaF[alpha], time = time, easing = easing, name = "alpha") -fun Animator.alpha(view: View, alpha: Double, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, alpha.toFloat(), time, easing) -fun Animator.alpha(view: View, alpha: Int, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, alpha.toFloat(), time, easing) - -fun Animator.rotateTo(view: View, angle: Angle, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::rotation[angle], time = time, easing = easing, name = "rotateTo") -fun Animator.rotateTo(view: View, rotation: () -> Angle, time: TimeSpan = this.defaultTime, lazyTime: (() -> TimeSpan)? = null, easing: Easing = this.defaultEasing) = __tween({ view::rotation[rotation()] }, time = time, lazyTime = lazyTime, easing = easing, name = "rotateTo") - -fun Animator.show(view: View, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, 1.0, time, easing) -fun Animator.hide(view: View, time: TimeSpan = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, 0.0, time, easing) +fun Animator.alpha(view: View, alpha: Float, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::alphaF[alpha], time = time, easing = easing, name = "alpha") +fun Animator.alpha(view: View, alpha: Double, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, alpha.toFloat(), time, easing) +fun Animator.alpha(view: View, alpha: Int, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, alpha.toFloat(), time, easing) + +fun Animator.rotateTo(view: View, angle: Angle, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = __tween(view::rotation[angle], time = time, easing = easing, name = "rotateTo") +fun Animator.rotateTo( + view: View, + rotation: () -> Angle, + time: Duration = this.defaultTime, + lazyTime: (() -> Duration)? = null, + easing: Easing = this.defaultEasing +) = __tween({ view::rotation[rotation()] }, time = time, lazyTime = lazyTime, easing = easing, name = "rotateTo") + +fun Animator.show(view: View, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, 1.0, time, easing) +fun Animator.hide(view: View, time: Duration = this.defaultTime, easing: Easing = this.defaultEasing) = alpha(view, 0.0, time, easing) private val VectorPath.length: Double get() = getCurves().length private val PointList.length: Double get() { diff --git a/korge/src/korlibs/korge/animate/AnimatorStateManager.kt b/korge/src/korlibs/korge/animate/AnimatorStateManager.kt index 6b5726b63e..065d122f09 100644 --- a/korge/src/korlibs/korge/animate/AnimatorStateManager.kt +++ b/korge/src/korlibs/korge/animate/AnimatorStateManager.kt @@ -10,13 +10,14 @@ import korlibs.io.lang.* import korlibs.math.* import korlibs.math.interpolation.* import kotlin.reflect.* +import kotlin.time.* @KorgeExperimental val View.animStateManager by Extra.PropertyThis { AnimatorStateManager(this) } @KorgeExperimental -data class AnimState(val transitions: List>, val time: TimeSpan = 0.5.seconds, val easing: Easing = Easing.LINEAR) { - constructor(vararg transitions: V2<*>, time: TimeSpan = 0.5.seconds, easing: Easing = Easing.LINEAR) : this(transitions.toList(), time = time, easing = easing) +data class AnimState(val transitions: List>, val time: Duration = 0.5.seconds, val easing: Easing = Easing.LINEAR) { + constructor(vararg transitions: V2<*>, time: Duration = 0.5.seconds, easing: Easing = Easing.LINEAR) : this(transitions.toList(), time = time, easing = easing) operator fun plus(other: AnimState): AnimState { return AnimState( @@ -29,6 +30,7 @@ data class AnimState(val transitions: List>, val time: TimeSpan = 0.5.seco class AnimatorStateManager(val view: View) { data class Entry(val v2: V2, val value: T) + val defaults = LinkedHashMap, Entry<*>>() private fun backup(state: AnimState) { @@ -44,7 +46,7 @@ class AnimatorStateManager(val view: View) { }) } - private var currentTime: TimeSpan = TimeSpan.ZERO + private var currentTime: Duration = TimeSpan.ZERO private var currentState: AnimState = AnimState() fun add(vararg states: AnimState) { @@ -76,7 +78,7 @@ class AnimatorStateManager(val view: View) { } } - fun update(dt: TimeSpan) { + fun update(dt: Duration) { val isStart = currentTime == TimeSpan.ZERO currentTime += dt var completedCount = 0 diff --git a/korge/src/korlibs/korge/audio/AudioChannel.kt b/korge/src/korlibs/korge/audio/AudioChannel.kt index 31dff8e8be..1c7840718f 100644 --- a/korge/src/korlibs/korge/audio/AudioChannel.kt +++ b/korge/src/korlibs/korge/audio/AudioChannel.kt @@ -8,6 +8,7 @@ import korlibs.math.interpolation.* import korlibs.time.* import kotlinx.coroutines.* import kotlin.reflect.* +import kotlin.time.* fun audioChannel(isLocal: Boolean = false): AudioChannel.Provider = if (isLocal) AudioChannel.ProviderLocal else AudioChannel.ProviderGlobal @@ -47,11 +48,11 @@ class AudioChannel(val name: String, val viewsContainer: ViewsContainer, val isL channel = null } - suspend fun fadeIn(time: TimeSpan = DEFAULT_FADE_TIME, easing: Easing = DEFAULT_FADE_EASING) { + suspend fun fadeIn(time: Duration = DEFAULT_FADE_TIME, easing: Easing = DEFAULT_FADE_EASING) { channel?.fadeIn(time, easing) } - suspend fun fadeOut(time: TimeSpan = DEFAULT_FADE_TIME, easing: Easing = DEFAULT_FADE_EASING) { + suspend fun fadeOut(time: Duration = DEFAULT_FADE_TIME, easing: Easing = DEFAULT_FADE_EASING) { channel?.fadeOut(time, easing) } } diff --git a/korge/src/korlibs/korge/component/StageComponent.kt b/korge/src/korlibs/korge/component/StageComponent.kt index 871e042b2e..9aae07a7ed 100644 --- a/korge/src/korlibs/korge/component/StageComponent.kt +++ b/korge/src/korlibs/korge/component/StageComponent.kt @@ -20,7 +20,7 @@ private class ViewStageComponent(val view: View) { private const val __VIEW_STAGE_COMPONENT_NAME = "__viewStageComponent" private val View.viewStageComponent by Extra.PropertyThis(__VIEW_STAGE_COMPONENT_NAME) { ViewStageComponent(this) } -fun T.onNewAttachDetach(onAttach: Views.(T) -> Unit = {}, onDetach: Views.(T) -> Unit = {}): Closeable { +fun T.onNewAttachDetach(onAttach: Views.(T) -> Unit = {}, onDetach: Views.(T) -> Unit = {}): AutoCloseable { val view = this val closeable = CancellableGroup() val viewStageComponent = this.viewStageComponent diff --git a/korge/src/korlibs/korge/input/DropFile.kt b/korge/src/korlibs/korge/input/DropFile.kt index 3effaa6f43..050567a582 100644 --- a/korge/src/korlibs/korge/input/DropFile.kt +++ b/korge/src/korlibs/korge/input/DropFile.kt @@ -14,5 +14,5 @@ import korlibs.korge.view.* * * [DropFileEvent.Type.DROP] - When the drop is effectively performed. [DropFileEvent.files] should be set here. * * [DropFileEvent.Type.END] - When the drag is cancelled or have been executed already. */ -fun View.onDropFile(handler: (DropFileEvent) -> Unit): Closeable = +fun View.onDropFile(handler: (DropFileEvent) -> Unit): AutoCloseable = onEvents(*DropFileEvent.Type.ALL) { handler(it) } diff --git a/korge/src/korlibs/korge/input/Input.kt b/korge/src/korlibs/korge/input/Input.kt index 6039c14dcb..27bc20fc56 100644 --- a/korge/src/korlibs/korge/input/Input.kt +++ b/korge/src/korlibs/korge/input/Input.kt @@ -17,6 +17,7 @@ import korlibs.event.Touch import korlibs.event.TouchEvent import korlibs.korge.internal.KorgeInternal import korlibs.math.geom.* +import kotlin.time.* //@Singleton @OptIn(KorgeInternal::class) @@ -124,19 +125,19 @@ class Input : Extra by Extra.Mixin() { } @KorgeInternal - fun startFrame(delta: TimeSpan) { + fun startFrame(delta: Duration) { this.extra?.clear() keys.startFrame(delta) } @KorgeInternal - fun endFrame(delta: TimeSpan) { + fun endFrame(delta: Duration) { this.clicked = false keys.endFrame(delta) endFrameOldKeys(delta) } - private fun endFrameOldKeys(delta: TimeSpan) { + private fun endFrameOldKeys(delta: Duration) { for (n in 0 until KEYCODES) { val prev = keysRawPrev[n] val curr = keysRaw[n] @@ -223,10 +224,10 @@ class InputKeys { } } - internal fun startFrame(delta: TimeSpan) { + internal fun startFrame(delta: Duration) { } - internal fun endFrame(delta: TimeSpan) { + internal fun endFrame(delta: Duration) { arraycopy(pressing, 0, pressingPrev, 0, pressing.size) } diff --git a/korge/src/korlibs/korge/input/KeysEvents.kt b/korge/src/korlibs/korge/input/KeysEvents.kt index b9874ae6d8..d9fb9c27c1 100644 --- a/korge/src/korlibs/korge/input/KeysEvents.kt +++ b/korge/src/korlibs/korge/input/KeysEvents.kt @@ -12,10 +12,12 @@ import korlibs.io.async.* import korlibs.io.lang.* import korlibs.math.interpolation.* import kotlin.native.concurrent.* +import kotlin.time.* -class KeysEvents(val view: View) : Closeable { +class KeysEvents(val view: View) : AutoCloseable { @PublishedApi internal lateinit var views: Views + @PublishedApi internal val coroutineContext get() = views.coroutineContext @@ -23,7 +25,7 @@ class KeysEvents(val view: View) : Closeable { private val onKeyUp = AsyncSignal() private val onKeyTyped = AsyncSignal() - fun KeyEvent.setFromKeys(key: Key, keys: InputKeys, dt: TimeSpan, type: KeyEvent.Type = KeyEvent.Type.DOWN): KeyEvent { + fun KeyEvent.setFromKeys(key: Key, keys: InputKeys, dt: Duration, type: KeyEvent.Type = KeyEvent.Type.DOWN): KeyEvent { this.type = type this.key = key this.keyCode = key.ordinal @@ -36,7 +38,7 @@ class KeysEvents(val view: View) : Closeable { } /** Executes [callback] on each frame when [key] is being pressed. When [dt] is provided, the [callback] is executed at that [dt] steps. */ - fun downFrame(keys: List, dt: TimeSpan = TimeSpan.NIL, callback: (ke: KeyEvent) -> Unit): Cancellable { + fun downFrame(keys: List, dt: Duration = TimeSpan.NIL, callback: (ke: KeyEvent) -> Unit): Cancellable { val ke = KeyEvent() return view.addOptFixedUpdater(dt) { dt -> if (::views.isInitialized) { @@ -50,9 +52,11 @@ class KeysEvents(val view: View) : Closeable { //if (view.input) } } - fun downFrame(vararg keys: Key, dt: TimeSpan = TimeSpan.NIL, callback: (ke: KeyEvent) -> Unit): Cancellable = + + fun downFrame(vararg keys: Key, dt: Duration = TimeSpan.NIL, callback: (ke: KeyEvent) -> Unit): Cancellable = downFrame(keys.toList(), dt, callback) - fun downFrame(key: Key, dt: TimeSpan = TimeSpan.NIL, callback: (ke: KeyEvent) -> Unit): Cancellable = + + fun downFrame(key: Key, dt: Duration = TimeSpan.NIL, callback: (ke: KeyEvent) -> Unit): Cancellable = downFrame(listOf(key), dt, callback) fun justDown(keys: List, callback: (ke: KeyEvent) -> Unit): Cancellable { @@ -70,10 +74,17 @@ class KeysEvents(val view: View) : Closeable { fun justDown(vararg keys: Key, callback: (ke: KeyEvent) -> Unit): Cancellable = justDown(keys.toList(), callback) + fun justDown(key: Key, callback: (ke: KeyEvent) -> Unit): Cancellable = justDown(listOf(key), callback) - fun downRepeating(keys: Set, maxDelay: TimeSpan = 500.milliseconds, minDelay: TimeSpan = 100.milliseconds, delaySteps: Int = 6, callback: suspend (ke: KeyEvent) -> Unit): Cancellable { + fun downRepeating( + keys: Set, + maxDelay: Duration = 500.milliseconds, + minDelay: Duration = 100.milliseconds, + delaySteps: Int = 6, + callback: suspend (ke: KeyEvent) -> Unit + ): Cancellable { val keys = keys.toList() val ke = KeyEvent() val currentStep = IntArray(keys.size) @@ -99,36 +110,73 @@ class KeysEvents(val view: View) : Closeable { } } - fun downRepeating(vararg keys: Key, maxDelay: TimeSpan = 500.milliseconds, minDelay: TimeSpan = 100.milliseconds, delaySteps: Int = 6, callback: suspend (ke: KeyEvent) -> Unit): Cancellable = + fun downRepeating( + vararg keys: Key, + maxDelay: Duration = 500.milliseconds, + minDelay: Duration = 100.milliseconds, + delaySteps: Int = 6, + callback: suspend (ke: KeyEvent) -> Unit + ): Cancellable = downRepeating(keys.toSet(), maxDelay, minDelay, delaySteps, callback) - fun downRepeating(key: Key, maxDelay: TimeSpan = 500.milliseconds, minDelay: TimeSpan = 100.milliseconds, delaySteps: Int = 6, callback: suspend (ke: KeyEvent) -> Unit): Cancellable = + + fun downRepeating( + key: Key, + maxDelay: Duration = 500.milliseconds, + minDelay: Duration = 100.milliseconds, + delaySteps: Int = 6, + callback: suspend (ke: KeyEvent) -> Unit + ): Cancellable = downRepeating(setOf(key), maxDelay, minDelay, delaySteps, callback) - fun down(keys: Set, callback: suspend (key: KeyEvent) -> Unit): Closeable = + fun down(keys: Set, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyDown { if (it.key in keys) callback(it) } - fun down(callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyDown { callback(it) } - fun down(key: Key, callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyDown { if (it.key == key) callback(it) } - fun down(vararg keys: Key, callback: suspend (key: KeyEvent) -> Unit): Closeable = down(keys.toSet(), callback) - fun downWithModifiers(keys: Set, ctrl: Boolean? = null, shift: Boolean? = null, alt: Boolean? = null, meta: Boolean? = null, callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyDown { e -> + fun down(callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyDown { callback(it) } + fun down(key: Key, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyDown { if (it.key == key) callback(it) } + fun down(vararg keys: Key, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = down(keys.toSet(), callback) + + fun downWithModifiers( + keys: Set, + ctrl: Boolean? = null, + shift: Boolean? = null, + alt: Boolean? = null, + meta: Boolean? = null, + callback: suspend (key: KeyEvent) -> Unit + ): AutoCloseable = onKeyDown { e -> if (e.key in keys && match(ctrl, e.ctrl) && match(shift, e.shift) && match(alt, e.alt) && match(meta, e.meta)) callback(e) } - fun downWithModifiers(key: Key, ctrl: Boolean? = null, shift: Boolean? = null, alt: Boolean? = null, meta: Boolean? = null, callback: suspend (key: KeyEvent) -> Unit): Closeable = + + fun downWithModifiers( + key: Key, + ctrl: Boolean? = null, + shift: Boolean? = null, + alt: Boolean? = null, + meta: Boolean? = null, + callback: suspend (key: KeyEvent) -> Unit + ): AutoCloseable = downWithModifiers(setOf(key), ctrl, shift, alt, meta, callback) - fun downWithModifiers(vararg keys: Key, ctrl: Boolean? = null, shift: Boolean? = null, alt: Boolean? = null, meta: Boolean? = null, callback: suspend (key: KeyEvent) -> Unit): Closeable = + + fun downWithModifiers( + vararg keys: Key, + ctrl: Boolean? = null, + shift: Boolean? = null, + alt: Boolean? = null, + meta: Boolean? = null, + callback: suspend (key: KeyEvent) -> Unit + ): AutoCloseable = downWithModifiers(keys.toSet(), ctrl, shift, alt, meta, callback) private fun match(pattern: Boolean?, value: Boolean) = (pattern == null || value == pattern) - fun up(keys: Set, callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyUp { e -> if (e.key in keys) callback(e) } - fun up(callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyUp { e -> callback(e) } - fun up(key: Key, callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyUp { e -> if (e.key == key) callback(e) } - fun up(vararg keys: Key, callback: suspend (key: KeyEvent) -> Unit): Closeable = up(keys.toSet(), callback) + fun up(keys: Set, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyUp { e -> if (e.key in keys) callback(e) } + fun up(callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyUp { e -> callback(e) } + fun up(key: Key, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyUp { e -> if (e.key == key) callback(e) } + fun up(vararg keys: Key, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = up(keys.toSet(), callback) - fun typed(keys: Set, callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyTyped { if (it.key in keys) callback(it) } - fun typed(callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyTyped { callback(it) } - fun typed(key: Key, callback: suspend (key: KeyEvent) -> Unit): Closeable = onKeyTyped { if (it.key == key) callback(it) } - fun typed(vararg keys: Key, callback: suspend (key: KeyEvent) -> Unit): Closeable = typed(keys.toSet(), callback) + fun typed(keys: Set, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyTyped { if (it.key in keys) callback(it) } + fun typed(callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyTyped { callback(it) } + fun typed(key: Key, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = onKeyTyped { if (it.key == key) callback(it) } + fun typed(vararg keys: Key, callback: suspend (key: KeyEvent) -> Unit): AutoCloseable = typed(keys.toSet(), callback) val closeable = CancellableGroup() diff --git a/korge/src/korlibs/korge/input/MouseDragComponent.kt b/korge/src/korlibs/korge/input/MouseDragComponent.kt index 155449278e..ef64f5d78e 100644 --- a/korge/src/korlibs/korge/input/MouseDragComponent.kt +++ b/korge/src/korlibs/korge/input/MouseDragComponent.kt @@ -4,6 +4,7 @@ import korlibs.io.lang.* import korlibs.korge.view.* import korlibs.math.geom.* import korlibs.time.* +import kotlin.time.* open class MouseDragInfo( val view: View, @@ -21,15 +22,17 @@ open class MouseDragInfo( override fun toString(): String = "MouseDragInfo(start=$start, end=$end, sx=$sx, sy=$sy, cx=$cx, cy=$cy)" lateinit var mouseEvents: MouseEvents - val elapsed: TimeSpan get() = time - startTime + val elapsed: Duration get() = time - startTime val localDXY: Point get() = localDXY(view) @Deprecated("") val localDX: Double get() = localDX(view) @Deprecated("") val localDY: Double get() = localDY(view) fun localDXY(view: View): Point = view.parent?.globalToLocalDelta(Point(0.0, 0.0), Point(dx, dy)) ?: Point(dx, dy) - @Deprecated("") fun localDX(view: View): Double = localDXY(view).x - @Deprecated("") fun localDY(view: View): Double = localDXY(view).y + @Deprecated("") + fun localDX(view: View): Double = localDXY(view).x + @Deprecated("") + fun localDY(view: View): Double = localDXY(view).y private var lastDx: Double = Double.NaN private var lastDy: Double = Double.NaN @@ -80,10 +83,10 @@ enum class MouseDragState { } data class OnMouseDragCloseable( - val onDownCloseable: Closeable, - val onUpAnywhereCloseable: Closeable, - val onMoveAnywhereCloseable: Closeable -) : Closeable { + val onDownCloseable: AutoCloseable, + val onUpAnywhereCloseable: AutoCloseable, + val onMoveAnywhereCloseable: AutoCloseable +) : AutoCloseable { override fun close() { onDownCloseable.close() onUpAnywhereCloseable.close() @@ -126,9 +129,11 @@ private fun T.onMouseDragInternal( sy = py info.reset() } + MouseDragState.END -> { dragging = false } + else -> Unit } cx = mousePos.x @@ -138,9 +143,9 @@ private fun T.onMouseDragInternal( callback(views(), info.set(dx, dy, state.isStart, state.isEnd, timeProvider.now(), sx, sy, cx, cy)) } - lateinit var onDownCloseable: Closeable - lateinit var onUpAnywhereCloseable: Closeable - lateinit var onMoveAnywhereCloseable: Closeable + lateinit var onDownCloseable: AutoCloseable + lateinit var onUpAnywhereCloseable: AutoCloseable + lateinit var onMoveAnywhereCloseable: AutoCloseable this.mouse { onDownCloseable = onDownCloseable { handle(it, MouseDragState.START) } onUpAnywhereCloseable = onUpAnywhereCloseable { handle(it, MouseDragState.END) } @@ -219,8 +224,8 @@ open class DraggableInfo(view: View) : MouseDragInfo(view) { } data class DraggableCloseable( - val onMouseDragCloseable: Closeable -): Closeable { + val onMouseDragCloseable: AutoCloseable +): AutoCloseable { override fun close() { onMouseDragCloseable.close() } diff --git a/korge/src/korlibs/korge/input/MouseEvents.kt b/korge/src/korlibs/korge/input/MouseEvents.kt index d599f2b778..28f1a8c6ee 100644 --- a/korge/src/korlibs/korge/input/MouseEvents.kt +++ b/korge/src/korlibs/korge/input/MouseEvents.kt @@ -16,6 +16,7 @@ import korlibs.time.* import kotlin.math.* import kotlin.native.concurrent.* import kotlin.reflect.* +import kotlin.time.* private var Input.mouseHitSearch by Extra.Property { false } private var Input.mouseHitResult by Extra.Property { null } @@ -24,7 +25,7 @@ private var Views.mouseDebugHandlerOnce by Extra.Property { Once() } private var Views.mouseDebugLastFrameClicked by Extra.Property { false } @OptIn(KorgeInternal::class) -class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { +class MouseEvents(val view: View) : Extra by Extra.Mixin(), AutoCloseable { init { view.mouseEnabled = true } @@ -67,7 +68,6 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { val matrix = views.windowToGlobalMatrix //println(scale) - var yy = 60.toDouble() * scale val lineHeight = 8.toDouble() * scale val mouseHit = mouseHitTest(views) @@ -224,14 +224,13 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { inline fun onScrollOutside(noinline handler: suspend (MouseEvents) -> Unit): MouseEvents = _mouseEvent(MouseEvents::scrollOutside, handler) - // Returns the Closeable after adding the handler to the corresponding Signal // in the MouseEvents. @PublishedApi internal inline fun _mouseEventCloseable( prop: KProperty1>, noinline handler: suspend (MouseEvents) -> Unit - ): Closeable { + ): AutoCloseable { return prop.get(this) .add { launchImmediately(this.coroutineContext) { handler(it) } } } @@ -324,6 +323,7 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { fun stopPropagation() { currentEvent?.stopPropagation() } + fun preventDefault() { currentEvent?.preventDefault() } @@ -406,6 +406,7 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { } } } + MouseEvent.Type.DOWN -> { if (!event.isAltDown) { views.mouseDebugLastFrameClicked = true @@ -420,6 +421,7 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { } } } + MouseEvent.Type.SCROLL -> { //this.lastEventScroll = event if (isOver) { @@ -458,7 +460,7 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { } } - fun update(views: Views, dt: TimeSpan) { + fun update(views: Views, dt: Duration) { if (!view.mouseEnabled) return this.views = views @@ -541,7 +543,6 @@ class MouseEvents(val view: View) : Extra by Extra.Mixin(), Closeable { // views.root.hitTest(input.mouse) //} - //var View.mouseEnabled by Extra.Property { true } val View.mouse: MouseEvents by Extra.PropertyThis { MouseEvents(this) } @@ -575,7 +576,7 @@ inline fun T.onOutOnOver( noinline out: @EventsDslMarker (MouseEvents) -> Unit, noinline over: @EventsDslMarker (MouseEvents) -> Unit ): T { - var component: Closeable? = null + var component: AutoCloseable? = null onOut { events -> component?.close() component = null @@ -617,9 +618,9 @@ inline fun T.onScroll(noinline handler: @EventsDslMarker suspend (Mo fun ViewsContainer.installMouseDebugExtensionOnce() = MouseEvents.installDebugExtensionOnce(views) -fun MouseEvents.doubleClick(callback: (MouseEvents) -> Unit): Closeable = multiClick(2, callback) +fun MouseEvents.doubleClick(callback: (MouseEvents) -> Unit): AutoCloseable = multiClick(2, callback) -fun MouseEvents.multiClick(count: Int, callback: (MouseEvents) -> Unit): Closeable { +fun MouseEvents.multiClick(count: Int, callback: (MouseEvents) -> Unit): AutoCloseable { var clickCount = 0 var lastClickTime = DateTime.EPOCH return this.click { diff --git a/korge/src/korlibs/korge/render/AgAutoFreeManager.kt b/korge/src/korlibs/korge/render/AgAutoFreeManager.kt index 663d2eeb8b..aab145c79b 100644 --- a/korge/src/korlibs/korge/render/AgAutoFreeManager.kt +++ b/korge/src/korlibs/korge/render/AgAutoFreeManager.kt @@ -6,9 +6,9 @@ import korlibs.io.lang.* // @TODO: This is pretty generic, we could expose it elsewhere class AgAutoFreeManager( -) : Closeable { +) : AutoCloseable { class Entry( - var closeable: Closeable? = null, + var closeable: AutoCloseable? = null, ) { fun closeAndReset() { closeable?.close() @@ -20,10 +20,10 @@ class AgAutoFreeManager( } private val entryPool = Pool(reset = { it.reset() }) { Entry() } - private val cachedCloseables = FastIdentityMap() + private val cachedCloseables = FastIdentityMap() private val availableInLastGC = fastArrayListOf() - fun reference(closeable: Closeable) { + fun reference(closeable: AutoCloseable) { cachedCloseables.getOrPut(closeable) { entryPool.alloc().also { it.closeable = closeable } } diff --git a/korge/src/korlibs/korge/render/AgBitmapTextureManager.kt b/korge/src/korlibs/korge/render/AgBitmapTextureManager.kt index 0edc3b22fe..ece88350c8 100644 --- a/korge/src/korlibs/korge/render/AgBitmapTextureManager.kt +++ b/korge/src/korlibs/korge/render/AgBitmapTextureManager.kt @@ -32,7 +32,7 @@ import korlibs.memory.unit.* @OptIn(KorgeInternal::class, KorgeExperimental::class) class AgBitmapTextureManager( val ag: AG -) : Closeable { +) : AutoCloseable { var maxCachedMemory = 0L /** Bitmaps to keep for some time even if not referenced in [framesBetweenGC] as long as the [maxCachedMemory] allows it */ diff --git a/korge/src/korlibs/korge/render/RenderContext.kt b/korge/src/korlibs/korge/render/RenderContext.kt index 44b4eeebe3..b8b162706f 100644 --- a/korge/src/korlibs/korge/render/RenderContext.kt +++ b/korge/src/korlibs/korge/render/RenderContext.kt @@ -51,7 +51,7 @@ class RenderContext( BoundsProvider by bp, AGFeatures by ag, DeviceDimensionsProvider by deviceDimensionsProvider, - Closeable + AutoCloseable { val quality: GameWindowQuality get() = windowConfig.quality @@ -318,7 +318,7 @@ class RenderContext( * * This can be use for example to automatically manage temporal/cached textures. */ - fun refGcCloseable(closeable: Closeable) = agAutoFreeManager.reference(closeable) + fun refGcCloseable(closeable: AutoCloseable) = agAutoFreeManager.reference(closeable) @PublishedApi internal fun beforeRender() { batch.beforeRender() diff --git a/korge/src/korlibs/korge/render/Texture.kt b/korge/src/korlibs/korge/render/Texture.kt index aa2c995555..0f9d1a1bfc 100644 --- a/korge/src/korlibs/korge/render/Texture.kt +++ b/korge/src/korlibs/korge/render/Texture.kt @@ -12,7 +12,7 @@ import korlibs.math.geom.slice.* class TextureBase( var base: AGTexture?, override var size: SizeInt, -) : Closeable, SizeableInt { +) : AutoCloseable, SizeableInt { constructor(base: AGTexture?, width: Int, height: Int) : this(base, SizeInt(width, height)) var width: Int get() = size.width; set(width) { size = SizeInt(width, height) } var height: Int get() = size.height; set(height) { size = SizeInt(width, height) } diff --git a/korge/src/korlibs/korge/scene/CompletableScene.kt b/korge/src/korlibs/korge/scene/CompletableScene.kt index a9b8d8e1df..b051ef1ce9 100644 --- a/korge/src/korlibs/korge/scene/CompletableScene.kt +++ b/korge/src/korlibs/korge/scene/CompletableScene.kt @@ -7,6 +7,7 @@ import korlibs.korge.view.SContainer import korlibs.io.async.launchImmediately import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred +import kotlin.time.* abstract class CompletableScene() : Scene() { private val deferred = CompletableDeferred() @@ -30,7 +31,7 @@ abstract class CompletableScene() : Scene() { suspend inline fun , R> SceneContainer.changeToResult( vararg injects: Any, - time: TimeSpan = 0.milliseconds, + time: Duration = 0.milliseconds, transition: Transition = AlphaTransition ): R { val instance = changeTo(T::class, *injects, time = time, transition = transition) diff --git a/korge/src/korlibs/korge/scene/Scene.kt b/korge/src/korlibs/korge/scene/Scene.kt index ec497e8775..b8ee5ec903 100644 --- a/korge/src/korlibs/korge/scene/Scene.kt +++ b/korge/src/korlibs/korge/scene/Scene.kt @@ -15,6 +15,7 @@ import korlibs.render.* import korlibs.time.* import kotlinx.coroutines.* import kotlin.coroutines.* +import kotlin.time.* /** * Acts as a controller. Subclasses must override at least one of: [sceneInit] or [sceneMain]. @@ -265,4 +266,4 @@ abstract class LogScene : Scene() { } } -suspend fun Scene.delay(time: TimeSpan) = sceneView.delay(time) +suspend fun Scene.delay(time: Duration) = sceneView.delay(time) diff --git a/korge/src/korlibs/korge/scene/SceneContainer.kt b/korge/src/korlibs/korge/scene/SceneContainer.kt index 46de6da265..509649d72f 100644 --- a/korge/src/korlibs/korge/scene/SceneContainer.kt +++ b/korge/src/korlibs/korge/scene/SceneContainer.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlin.reflect.* +import kotlin.time.* /** * Creates a new [SceneContainer], allowing to configure with [callback], and attaches the newly created container to the receiver this [Container] @@ -109,26 +110,26 @@ class SceneContainer( // Async versions /** Async variant returning a [Deferred] for [changeTo] */ - inline fun changeToAsync(vararg injects: Any, time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Deferred + inline fun changeToAsync(vararg injects: Any, time: Duration = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { changeTo(*injects, time = time, transition = transition) } /** Async variant returning a [Deferred] for [changeTo] */ - fun changeToAsync(clazz: KClass, vararg injects: Any, time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Deferred + fun changeToAsync(clazz: KClass, vararg injects: Any, time: Duration = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { changeTo(clazz, *injects, time = time, transition = transition) } /** Async variant returning a [Deferred] for [pushTo] */ - inline fun pushToAsync(vararg injects: Any, time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Deferred + inline fun pushToAsync(vararg injects: Any, time: Duration = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { pushTo(*injects, time = time, transition = transition) } /** Async variant returning a [Deferred] for [pushTo] */ - fun pushToAsync(clazz: KClass, vararg injects: Any, time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Deferred + fun pushToAsync(clazz: KClass, vararg injects: Any, time: Duration = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { pushTo(clazz, *injects, time = time, transition = transition) } /** Async variant returning a [Deferred] for [back] */ - suspend fun backAsync(time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { back(time, transition) } + suspend fun backAsync(time: Duration = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { back(time, transition) } /** Async variant returning a [Deferred] for [forward] */ - suspend fun forwardAsync(time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { forward(time, transition) } + suspend fun forwardAsync(time: Duration = 0.seconds, transition: Transition = defaultTransition): Deferred = CoroutineScope(coroutineContext).async { forward(time, transition) } /** * Changes to the [T] [Scene], with a set of optional [injects] instances during [time] time, and with [transition]. @@ -136,9 +137,9 @@ class SceneContainer( */ suspend inline fun changeTo( vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition - ): T = changeTo(T::class, *injects, time = time, transition = transition) + ): T = changeTo(T::class, *injects, time = time, transition = transition) /** * Pushes the [T] [Scene], with a set of optional [injects] instances during [time] time, and with [transition]. @@ -147,15 +148,15 @@ class SceneContainer( */ suspend inline fun pushTo( vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition - ) = pushTo(T::class, *injects, time = time, transition = transition) + ) = pushTo(T::class, *injects, time = time, transition = transition) /** * Returns to the previous pushed Scene with [pushTo] in [time] time, and with [transition]. * This method waits until the [transition] has been completed, and returns the old [Scene] instance. */ - suspend fun back(time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Scene { + suspend fun back(time: Duration = 0.seconds, transition: Transition = defaultTransition): Scene { visitPos-- return _changeTo(visitStack.getOrNull(visitPos) ?: EMPTY_VISIT_ENTRY, time = time, transition = transition) } @@ -164,7 +165,7 @@ class SceneContainer( * Returns to the next pushed Scene with [pushTo] in [time] time, and with [transition]. * This method waits until the [transition] has been completed, and returns the old [Scene] instance. */ - suspend fun forward(time: TimeSpan = 0.seconds, transition: Transition = defaultTransition): Scene { + suspend fun forward(time: Duration = 0.seconds, transition: Transition = defaultTransition): Scene { visitPos++ return _changeTo(visitStack.getOrNull(visitPos) ?: EMPTY_VISIT_ENTRY, time = time, transition = transition) } @@ -177,9 +178,9 @@ class SceneContainer( suspend fun pushTo( clazz: KClass, vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition - ): T { + ): T { pushEntry(VisitEntry(clazz, injects.toList())) return _changeTo(clazz, *injects, time = time, transition = transition) } @@ -201,16 +202,16 @@ class SceneContainer( suspend fun changeTo( clazz: KClass, vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition - ): T { + ): T { setCurrent(VisitEntry(clazz, injects.toList())) return _changeTo(clazz, *injects, time = time, transition = transition) } suspend inline fun changeTo( vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition, crossinline gen: Injector.() -> T, ): T { @@ -221,7 +222,7 @@ class SceneContainer( suspend inline fun changeTo( crossinline gen: (Injector) -> T, vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition ): T { return changeTo(T::class, gen, injects = injects, time = time, transition = transition, remap = true) @@ -231,7 +232,7 @@ class SceneContainer( clazz: KClass, crossinline gen: Injector.() -> T, vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition, remap: Boolean = false ): T { @@ -255,15 +256,15 @@ class SceneContainer( private suspend fun _changeTo( entry: VisitEntry, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition - ): Scene = _changeTo(entry.clazz, *entry.injects.toTypedArray(), time = time, transition = transition) as Scene + ): Scene = _changeTo(entry.clazz, *entry.injects.toTypedArray(), time = time, transition = transition) as Scene /** Check [Scene] for details of the lifecycle. */ private suspend fun _changeTo( clazz: KClass, vararg injects: Any, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition ): T { return changeTo(clazz, { get(clazz) }, *injects, time = time, transition = transition) @@ -272,7 +273,7 @@ class SceneContainer( /** Check [Scene] for details of the lifecycle. */ @PublishedApi internal suspend fun _changeTo( newScene: T, - time: TimeSpan = 0.seconds, + time: Duration = 0.seconds, transition: Transition = defaultTransition ): T { val oldScene = currentScene diff --git a/korge/src/korlibs/korge/service/vibration/NativeVibration.kt b/korge/src/korlibs/korge/service/vibration/NativeVibration.kt index a7ab78b7ca..6b43be13fd 100644 --- a/korge/src/korlibs/korge/service/vibration/NativeVibration.kt +++ b/korge/src/korlibs/korge/service/vibration/NativeVibration.kt @@ -4,6 +4,7 @@ import korlibs.datastructure.extraPropertyThis import korlibs.time.TimeSpan import korlibs.korge.view.Views import kotlin.native.concurrent.ThreadLocal +import kotlin.time.* val Views.vibration by extraPropertyThis { NativeVibration(this) } @@ -18,12 +19,12 @@ expect class NativeVibration constructor(views: Views) { * @param amplitudes list of intensities of the vibration. A `0.2` results in 20% vibration power. * Only supported on Android target. Ignored if the size is not equal with the timings. */ - fun vibratePattern(timings: Array, amplitudes: Array = emptyArray()) + fun vibratePattern(timings: Array, amplitudes: Array = emptyArray()) /** * @param time vibration duration in milliseconds * @param amplitude percentage intensity of the vibration. A `0.2` results in 20% vibration power. * Only supported on Android target. */ - fun vibrate(time: TimeSpan, amplitude: Double = 1.0) + fun vibrate(time: Duration, amplitude: Double = 1.0) } diff --git a/korge/src/korlibs/korge/style/ViewStyles.kt b/korge/src/korlibs/korge/style/ViewStyles.kt index fa06fd81d0..1c38b52dcc 100644 --- a/korge/src/korlibs/korge/style/ViewStyles.kt +++ b/korge/src/korlibs/korge/style/ViewStyles.kt @@ -4,8 +4,8 @@ import korlibs.datastructure.* import korlibs.image.color.* import korlibs.image.font.* import korlibs.image.text.* -import korlibs.io.concurrent.atomic.* import korlibs.korge.view.* +import kotlinx.atomicfu.* import kotlin.reflect.* val View.styles: ViewStyles by Extra.PropertyThis { ViewStyles(this) } @@ -24,7 +24,7 @@ interface StyleableView { class ViewStyles(val view: View) { @PublishedApi internal var data: LinkedHashMap? = null - var updating = KorAtomicInt(0) + var updating = atomic(0) fun getProp(prop: KProperty, default: T): T = (data?.get(prop.name) as? T?) ?: view.parent?.styles?.getProp(prop, default) ?: default diff --git a/korge/src/korlibs/korge/tests/TestCoroutineDispatcher.kt b/korge/src/korlibs/korge/tests/TestCoroutineDispatcher.kt index d2762a470d..2273913850 100644 --- a/korge/src/korlibs/korge/tests/TestCoroutineDispatcher.kt +++ b/korge/src/korlibs/korge/tests/TestCoroutineDispatcher.kt @@ -5,10 +5,11 @@ import korlibs.datastructure.lock.* import korlibs.time.* import kotlinx.coroutines.* import kotlin.coroutines.* +import kotlin.time.* @OptIn(InternalCoroutinesApi::class) @Deprecated("") -class TestCoroutineDispatcher(val frameTime: TimeSpan = 16.milliseconds) : +class TestCoroutineDispatcher(val frameTime: Duration = 16.milliseconds) : //CoroutineDispatcher(), ContinuationInterceptor, Delay, DelayFrame { CoroutineDispatcher(), ContinuationInterceptor, Delay { var time = 0L; private set @@ -78,7 +79,7 @@ class TestCoroutineDispatcher(val frameTime: TimeSpan = 16.milliseconds) : } @Deprecated("Use loop instead if possible") - suspend fun step(time: TimeSpan) { + suspend fun step(time: Duration) { this.time += time.millisecondsLong while (true) { val task = lock { if (tasks.isNotEmpty() && this.time >= tasks.head.time) tasks.removeHead() else null } ?: break diff --git a/korge/src/korlibs/korge/tests/ViewsForTesting.kt b/korge/src/korlibs/korge/tests/ViewsForTesting.kt index ad7d365ab4..2b4c0e0dca 100644 --- a/korge/src/korlibs/korge/tests/ViewsForTesting.kt +++ b/korge/src/korlibs/korge/tests/ViewsForTesting.kt @@ -18,12 +18,13 @@ import korlibs.render.* import korlibs.time.* import kotlinx.coroutines.* import kotlin.jvm.* +import kotlin.time.* import kotlin.time.Duration.Companion.milliseconds expect fun enrichTestGameWindow(window: ViewsForTesting.TestGameWindow) open class ViewsForTesting( - val frameTime: TimeSpan = 10.milliseconds, + val frameTime: Duration = 10.milliseconds, val windowSize: Size = DefaultViewport.SIZE, val virtualSize: Size = windowSize, val defaultDevicePixelRatio: Double = 1.0, @@ -282,8 +283,8 @@ open class ViewsForTesting( } fun viewsTest( - timeout: TimeSpan? = DEFAULT_SUSPEND_TEST_TIMEOUT, - frameTime: TimeSpan = this.frameTime, + timeout: Duration? = DEFAULT_SUSPEND_TEST_TIMEOUT, + frameTime: Duration = this.frameTime, cond: () -> Boolean = { Platform.isJvm && !Platform.isAndroid }, //devicePixelRatio: Double = defaultDevicePixelRatio, forceRenderEveryFrame: Boolean = true, @@ -334,8 +335,8 @@ open class ViewsForTesting( inline fun sceneTest( config: Korge? = null, noinline configureInjector: Injector.() -> Unit = {}, - timeout: TimeSpan? = DEFAULT_SUSPEND_TEST_TIMEOUT, - frameTime: TimeSpan = this.frameTime, + timeout: Duration? = DEFAULT_SUSPEND_TEST_TIMEOUT, + frameTime: Duration = this.frameTime, crossinline block: suspend S.() -> Unit ): AsyncEntryPointResult = viewsTest(timeout, frameTime) { config?.apply { diff --git a/korge/src/korlibs/korge/text/TextEditController.kt b/korge/src/korlibs/korge/text/TextEditController.kt index 93017ec9bb..652b1eb889 100644 --- a/korge/src/korlibs/korge/text/TextEditController.kt +++ b/korge/src/korlibs/korge/text/TextEditController.kt @@ -30,7 +30,7 @@ class TextEditController( val caretContainer: Container = textView, val eventHandler: View = textView, val bg: RenderableView? = null, -) : Closeable, UIFocusable, ISoftKeyboardConfig by SoftKeyboardConfig() { +) : AutoCloseable, UIFocusable, ISoftKeyboardConfig by SoftKeyboardConfig() { init { textView.focusable = this } diff --git a/korge/src/korlibs/korge/time/TimeSpanExt.kt b/korge/src/korlibs/korge/time/TimeSpanExt.kt index a282d0bc44..f755022897 100644 --- a/korge/src/korlibs/korge/time/TimeSpanExt.kt +++ b/korge/src/korlibs/korge/time/TimeSpanExt.kt @@ -3,5 +3,6 @@ package korlibs.korge.time import korlibs.time.TimeSpan import korlibs.time.milliseconds import korlibs.math.interpolation.* +import kotlin.time.* -fun Ratio.interpolate(a: TimeSpan, b: TimeSpan): TimeSpan = this.interpolate(a.milliseconds, b.milliseconds).milliseconds +fun Ratio.interpolate(a: Duration, b: Duration): Duration = this.interpolate(a.milliseconds, b.milliseconds).milliseconds diff --git a/korge/src/korlibs/korge/time/TimerComponents.kt b/korge/src/korlibs/korge/time/TimerComponents.kt index 6689f24612..38663c7b9b 100644 --- a/korge/src/korlibs/korge/time/TimerComponents.kt +++ b/korge/src/korlibs/korge/time/TimerComponents.kt @@ -8,17 +8,18 @@ import korlibs.io.lang.* import korlibs.math.* import kotlinx.coroutines.* import kotlin.coroutines.* +import kotlin.time.* -private typealias TimerCallback = (TimeSpan) -> Unit +private typealias TimerCallback = (Duration) -> Unit inline class TimerRef(val index: Int) class TimerComponents(val view: View) { - private val _timers = arrayListOf<((TimeSpan) -> Unit)?>() + private val _timers = arrayListOf<((Duration) -> Unit)?>() private val _autoRemove = IntArrayList() private val _freeIndices = IntArrayList() - private var updater: Closeable? = null + private var updater: AutoCloseable? = null private fun ensureUpdater() { if (updater != null) return @@ -36,7 +37,7 @@ class TimerComponents(val view: View) { } } - private fun addTimer(autoRemove: Boolean, callback: (TimeSpan) -> Unit): TimerRef { + private fun addTimer(autoRemove: Boolean, callback: (Duration) -> Unit): TimerRef { if (_freeIndices.isNotEmpty()) { val index = _freeIndices.removeAt(_freeIndices.size - 1) _timers[index] = callback @@ -56,10 +57,10 @@ class TimerComponents(val view: View) { _freeIndices.add(ref.index) } - suspend fun wait(time: TimeSpan): Unit = suspendCancellableCoroutine { c -> timeout(time) { c.resume(Unit) } } + suspend fun wait(time: Duration): Unit = suspendCancellableCoroutine { c -> timeout(time) { c.resume(Unit) } } suspend fun waitFrame() = suspendCoroutine { c -> addTimer(true) { c.resume(Unit) } } - private fun _interval(time: TimeSpan, repeat: Boolean, callback: () -> Unit = {}): Closeable { + private fun _interval(time: Duration, repeat: Boolean, callback: () -> Unit = {}): AutoCloseable { var elapsed = 0.milliseconds var ref = TimerRef(-1) ref = addTimer(false) { dt -> @@ -74,9 +75,9 @@ class TimerComponents(val view: View) { return Closeable { removeTimer(ref) } } - fun timeout(time: TimeSpan, callback: () -> Unit = {}): Closeable = _interval(time, false, callback) - fun interval(time: TimeSpan, callback: () -> Unit = {}): Closeable = _interval(time, true, callback) - fun intervalAndNow(time: TimeSpan, callback: () -> Unit): Closeable { + fun timeout(time: Duration, callback: () -> Unit = {}): AutoCloseable = _interval(time, false, callback) + fun interval(time: Duration, callback: () -> Unit = {}): AutoCloseable = _interval(time, true, callback) + fun intervalAndNow(time: Duration, callback: () -> Unit): AutoCloseable { callback() return interval(time, callback) } @@ -84,9 +85,9 @@ class TimerComponents(val view: View) { val View.timers: TimerComponents by Extra.PropertyThis("__ViewTimerComponents") { TimerComponents(this) } -fun View.timeout(time: TimeSpan, callback: () -> Unit): Closeable = this.timers.timeout(time, callback) -fun View.interval(time: TimeSpan, callback: () -> Unit): Closeable = this.timers.interval(time, callback) -fun View.intervalAndNow(time: TimeSpan, callback: () -> Unit): Closeable = this.timers.intervalAndNow(time, callback) +fun View.timeout(time: Duration, callback: () -> Unit): AutoCloseable = this.timers.timeout(time, callback) +fun View.interval(time: Duration, callback: () -> Unit): AutoCloseable = this.timers.interval(time, callback) +fun View.intervalAndNow(time: Duration, callback: () -> Unit): AutoCloseable = this.timers.intervalAndNow(time, callback) -suspend fun View.delay(time: TimeSpan) = this.timers.wait(time) +suspend fun View.delay(time: Duration) = this.timers.wait(time) suspend fun View.delayFrame() = this.timers.waitFrame() diff --git a/korge/src/korlibs/korge/tween/tween.kt b/korge/src/korlibs/korge/tween/tween.kt index 8d6608b256..bbb2a827fc 100644 --- a/korge/src/korlibs/korge/tween/tween.kt +++ b/korge/src/korlibs/korge/tween/tween.kt @@ -11,15 +11,16 @@ import korlibs.math.interpolation.* import korlibs.time.* import kotlinx.coroutines.* import kotlin.coroutines.* +import kotlin.time.* class TweenComponent( val view: BaseView, private val vs: List>, - val time: TimeSpan = TimeSpan.NIL, + val time: Duration = TimeSpan.NIL, val easing: Easing = DEFAULT_EASING, val callback: (Float) -> Unit, val c: CancellableContinuation?, - val waitTime: TimeSpan = TimeSpan.NIL, + val waitTime: Duration = TimeSpan.NIL, val autoInvalidate: Boolean = true ) { var elapsed = 0.0.milliseconds @@ -28,7 +29,7 @@ class TweenComponent( var done = false var resumed = false - var updater: Closeable? = view.onEvent(UpdateEvent) { it -> _update(it.deltaTime) } + var updater: AutoCloseable? = view.onEvent(UpdateEvent) { it -> _update(it.deltaTime) } init { @@ -39,7 +40,7 @@ class TweenComponent( _update(TimeSpan.ZERO) } - private fun _update(dt: TimeSpan) { + private fun _update(dt: Duration) { if (autoInvalidate) { view.invalidateRender() } @@ -83,7 +84,7 @@ class TweenComponent( //println("TWEEN COMPLETED[$this, $vs]: $elapsed") } - fun setTo(elapsed: TimeSpan) { + fun setTo(elapsed: Duration) { if (elapsed == 0.milliseconds) { vs.fastForEach { v -> v.init() @@ -113,9 +114,9 @@ class TweenComponent( */ suspend fun BaseView?.tween( vararg vs: V2<*>, - time: TimeSpan = DEFAULT_TIME, + time: Duration = DEFAULT_TIME, easing: Easing = DEFAULT_EASING, - waitTime: TimeSpan = TimeSpan.NIL, + waitTime: Duration = TimeSpan.NIL, timeout: Boolean = false, autoInvalidate: Boolean = true, // @TODO: We should use Ratio here as callback at some point @@ -139,9 +140,9 @@ suspend fun BaseView?.tween( fun BaseView?.tweenNoWait( vararg vs: V2<*>, - time: TimeSpan = DEFAULT_TIME, + time: Duration = DEFAULT_TIME, easing: Easing = DEFAULT_EASING, - waitTime: TimeSpan = TimeSpan.NIL, + waitTime: Duration = TimeSpan.NIL, callback: (Float) -> Unit = { } ): TweenComponent? { if (this == null) return null @@ -150,9 +151,9 @@ fun BaseView?.tweenNoWait( suspend fun QView.tween( vararg vs: V2<*>, - time: TimeSpan = DEFAULT_TIME, + time: Duration = DEFAULT_TIME, easing: Easing = DEFAULT_EASING, - waitTime: TimeSpan = TimeSpan.NIL, + waitTime: Duration = TimeSpan.NIL, callback: (Float) -> Unit = { } ) { if (isEmpty()) { @@ -172,18 +173,18 @@ internal val DEFAULT_EASING = Easing.EASE_IN_OUT_QUAD internal val DEFAULT_TIME = 1.seconds suspend fun BaseView?.tweenAsync( - vararg vs: V2<*>, - time: TimeSpan = DEFAULT_TIME, - easing: Easing = DEFAULT_EASING, - waitTime: TimeSpan = TimeSpan.NIL, - callback: (Float) -> Unit = {} + vararg vs: V2<*>, + time: Duration = DEFAULT_TIME, + easing: Easing = DEFAULT_EASING, + waitTime: Duration = TimeSpan.NIL, + callback: (Float) -> Unit = {} ): Deferred = asyncImmediately(coroutineContext) { tween(*vs, time = time, easing = easing, waitTime = waitTime, callback = callback) } fun BaseView?.tweenAsync( - vararg vs: V2<*>, - coroutineContext: CoroutineContext, - time: TimeSpan = DEFAULT_TIME, - easing: Easing = DEFAULT_EASING, - waitTime: TimeSpan = TimeSpan.NIL, + vararg vs: V2<*>, + coroutineContext: CoroutineContext, + time: Duration = DEFAULT_TIME, + easing: Easing = DEFAULT_EASING, + waitTime: Duration = TimeSpan.NIL, callback: (Float) -> Unit = {} ) = asyncImmediately(coroutineContext) { tween(*vs, time = time, easing = easing, waitTime = waitTime, callback = callback) } diff --git a/korge/src/korlibs/korge/tween/tweenbase.kt b/korge/src/korlibs/korge/tween/tweenbase.kt index af8dddfe61..16d31478d7 100644 --- a/korge/src/korlibs/korge/tween/tweenbase.kt +++ b/korge/src/korlibs/korge/tween/tweenbase.kt @@ -10,6 +10,7 @@ import korlibs.time.* import kotlin.jvm.* import kotlin.native.concurrent.* import kotlin.reflect.* +import kotlin.time.* @Suppress("UNCHECKED_CAST") data class V2( @@ -18,17 +19,17 @@ data class V2( val end: V, val interpolator: (Ratio, V, V) -> V, val includeStart: Boolean, - val startTime: TimeSpan = 0.nanoseconds, - val duration: TimeSpan = TimeSpan.NIL, + val startTime: Duration = 0.nanoseconds, + val duration: Duration = TimeSpan.NIL, private val initialization: (() -> Unit)? = null, ) { val endTime = startTime + duration.coalesce { 0.nanoseconds } @Deprecated("", ReplaceWith("getDuration(duration)"), level = DeprecationLevel.HIDDEN) - fun duration(default: TimeSpan): TimeSpan = getDuration(default) + fun duration(default: Duration): Duration = getDuration(default) - fun getDuration(default: TimeSpan): TimeSpan = if (duration.isNil) default else duration - fun endTime(default: TimeSpan): TimeSpan = if (duration.isNil) default else endTime + fun getDuration(default: Duration): Duration = if (duration.isNil) default else duration + fun endTime(default: Duration): Duration = if (duration.isNil) default else endTime fun init(): Unit { if (!includeStart) { @@ -63,7 +64,9 @@ private object V2CallbackSupport { private class V2CallbackTSupport(var dummy: T) -fun V2Callback(init: () -> Unit = {}, callback: (Ratio) -> Unit): V2 = V2(V2CallbackSupport::dummy, Unit, Unit, { ratio, _, _ -> callback(ratio) }, true, initialization = init) +fun V2Callback(init: () -> Unit = {}, callback: (Ratio) -> Unit): V2 = + V2(V2CallbackSupport::dummy, Unit, Unit, { ratio, _, _ -> callback(ratio) }, true, initialization = init) + fun V2CallbackT(initial: T, init: () -> Unit = {}, callback: (Ratio) -> T): V2 { return V2(V2CallbackTSupport(initial)::dummy, initial, initial, { ratio, _, _ -> callback(ratio) }, true, initialization = init) } @@ -85,6 +88,7 @@ fun KMutableProperty0.incr(delta: Vector2D): V2 { start = this.get() }) } + fun KMutableProperty0.incr(dx: Double, dy: Double): V2 { val start: MPoint = MPoint(0, 0) val value: MPoint = MPoint(0, 0) @@ -95,6 +99,7 @@ fun KMutableProperty0.incr(dx: Double, dy: Double): V2 { start.copyFrom(this.get()) }) } + fun KMutableProperty0.incr(dx: Number, dy: Number): V2 = incr(dx.toDouble(), dy.toDouble()) fun KMutableProperty0.incr(incr: MPoint): V2 = incr(incr.x, incr.y) @@ -103,46 +108,71 @@ fun KMutableProperty0.incr(incr: Double): V2 = V2(this, 0.0, 0.0 //println("INTERPOLATE: it=$it, start=$start, incr=$incr") it.interpolate(start, start + incr) }, includeStart = false) + fun KMutableProperty0.incr(incr: Angle): V2 = V2(this, Angle.ZERO, Angle.ZERO, interpolator = { it, start, _ -> it.interpolateAngleDenormalized(start, start + incr) }, includeStart = false) @JvmName("getInt") operator fun KMutableProperty0.get(end: Int) = V2(this, this.get(), end, ::_interpolateInt, includeStart = false) + @JvmName("getInt") operator fun KMutableProperty0.get(initial: Int, end: Int) = V2(this, initial, end, ::_interpolateInt, includeStart = true) @JvmName("getMutableProperty") operator fun > KMutableProperty0.get(end: V) = V2(this, this.get(), end, ::_interpolateInterpolable, includeStart = false) + @JvmName("getMutableProperty") operator fun > KMutableProperty0.get(initial: V, end: V) = V2(this, initial, end, ::_interpolateInterpolable, includeStart = true) -@JvmName("getMutablePropertyPoint") operator fun KMutableProperty0.get(end: Matrix) = V2(this, this.get(), end, ::_interpolateMatrix, includeStart = false) -@JvmName("getMutablePropertyPoint") operator fun KMutableProperty0.get(initial: Matrix, end: Matrix) = V2(this, initial, end, ::_interpolateMatrix, includeStart = true) - -@JvmName("getMutablePropertyPoint") operator fun KMutableProperty0.get(end: Point) = V2(this, this.get(), end, ::_interpolatePoint, includeStart = false) -@JvmName("getMutablePropertyPoint") operator fun KMutableProperty0.get(initial: Point, end: Point) = V2(this, initial, end, ::_interpolatePoint, includeStart = true) - -@JvmName("getMutablePropertySize") operator fun KMutableProperty0.get(end: Size) = V2(this, this.get(), end, ::_interpolateSize, includeStart = false) -@JvmName("getMutablePropertySize") operator fun KMutableProperty0.get(initial: Size, end: Size) = V2(this, initial, end, ::_interpolateSize, includeStart = true) - -@JvmName("getMutablePropertyScale") operator fun KMutableProperty0.get(end: Scale) = V2(this, this.get(), end, ::_interpolateScale, includeStart = false) -@JvmName("getMutablePropertyScale") operator fun KMutableProperty0.get(initial: Scale, end: Scale) = V2(this, initial, end, ::_interpolateScale, includeStart = true) - -@PublishedApi internal fun _interpolate(ratio: Ratio, l: Double, r: Double): Double = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateInt(ratio: Ratio, l: Int, r: Int): Int = ratio.interpolate(l, r) -@PublishedApi internal fun > _interpolateInterpolable(ratio: Ratio, l: V, r: V): V = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateRatio(ratio: Ratio, l: Ratio, r: Ratio): Ratio = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateMatrix(ratio: Ratio, l: Matrix, r: Matrix): Matrix = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolatePoint(ratio: Ratio, l: Point, r: Point): Point = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateSize(ratio: Ratio, l: Size, r: Size): Size = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateScale(ratio: Ratio, l: Scale, r: Scale): Scale = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateFloat(ratio: Ratio, l: Float, r: Float): Float = ratio.interpolate(l, r) -@PublishedApi internal fun _interpolateColor(ratio: Ratio, l: RGBA, r: RGBA): RGBA = RGBA.mixRgba(l, r, ratio) -@PublishedApi internal fun _interpolateAngle(ratio: Ratio, l: Angle, r: Angle): Angle = ratio.interpolateAngleNormalized(l, r) -@PublishedApi internal fun _interpolateAngleDenormalized(ratio: Ratio, l: Angle, r: Angle): Angle = ratio.interpolateAngleDenormalized(l, r) -@PublishedApi internal fun _interpolateTimeSpan(ratio: Ratio, l: TimeSpan, r: TimeSpan): TimeSpan = _interpolate(ratio, l.milliseconds, r.milliseconds).milliseconds -@PublishedApi internal fun _interpolateColorAdd(ratio: Ratio, l: ColorAdd, r: ColorAdd): ColorAdd = ColorAdd( +@JvmName("getMutablePropertyPoint") +operator fun KMutableProperty0.get(end: Matrix) = V2(this, this.get(), end, ::_interpolateMatrix, includeStart = false) +@JvmName("getMutablePropertyPoint") +operator fun KMutableProperty0.get(initial: Matrix, end: Matrix) = V2(this, initial, end, ::_interpolateMatrix, includeStart = true) + +@JvmName("getMutablePropertyPoint") +operator fun KMutableProperty0.get(end: Point) = V2(this, this.get(), end, ::_interpolatePoint, includeStart = false) +@JvmName("getMutablePropertyPoint") +operator fun KMutableProperty0.get(initial: Point, end: Point) = V2(this, initial, end, ::_interpolatePoint, includeStart = true) + +@JvmName("getMutablePropertySize") +operator fun KMutableProperty0.get(end: Size) = V2(this, this.get(), end, ::_interpolateSize, includeStart = false) +@JvmName("getMutablePropertySize") +operator fun KMutableProperty0.get(initial: Size, end: Size) = V2(this, initial, end, ::_interpolateSize, includeStart = true) + +@JvmName("getMutablePropertyScale") +operator fun KMutableProperty0.get(end: Scale) = V2(this, this.get(), end, ::_interpolateScale, includeStart = false) +@JvmName("getMutablePropertyScale") +operator fun KMutableProperty0.get(initial: Scale, end: Scale) = V2(this, initial, end, ::_interpolateScale, includeStart = true) + +@PublishedApi +internal fun _interpolate(ratio: Ratio, l: Double, r: Double): Double = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateInt(ratio: Ratio, l: Int, r: Int): Int = ratio.interpolate(l, r) +@PublishedApi +internal fun > _interpolateInterpolable(ratio: Ratio, l: V, r: V): V = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateRatio(ratio: Ratio, l: Ratio, r: Ratio): Ratio = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateMatrix(ratio: Ratio, l: Matrix, r: Matrix): Matrix = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolatePoint(ratio: Ratio, l: Point, r: Point): Point = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateSize(ratio: Ratio, l: Size, r: Size): Size = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateScale(ratio: Ratio, l: Scale, r: Scale): Scale = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateFloat(ratio: Ratio, l: Float, r: Float): Float = ratio.interpolate(l, r) +@PublishedApi +internal fun _interpolateColor(ratio: Ratio, l: RGBA, r: RGBA): RGBA = RGBA.mixRgba(l, r, ratio) +@PublishedApi +internal fun _interpolateAngle(ratio: Ratio, l: Angle, r: Angle): Angle = ratio.interpolateAngleNormalized(l, r) +@PublishedApi +internal fun _interpolateAngleDenormalized(ratio: Ratio, l: Angle, r: Angle): Angle = ratio.interpolateAngleDenormalized(l, r) +@PublishedApi +internal fun _interpolateTimeSpan(ratio: Ratio, l: Duration, r: Duration): Duration = _interpolate(ratio, l.milliseconds, r.milliseconds).milliseconds +@PublishedApi +internal fun _interpolateColorAdd(ratio: Ratio, l: ColorAdd, r: ColorAdd): ColorAdd = ColorAdd( ratio.interpolate(l.r, r.r), ratio.interpolate(l.g, r.g), ratio.interpolate(l.b, r.b), @@ -154,21 +184,26 @@ operator fun > KMutableProperty0.get(initial: V, end: V) // V2(this, initial.toFloat(), end.toFloat(), ::_interpolateFloat) @JvmName("getPoint") -inline operator fun KMutableProperty0.get(path: VectorPath, includeLastPoint: Boolean = path.isLastCommandClose, reversed: Boolean = false): V2 = this[path.getCurves().getEquidistantPoints().also { - //println("points.lastX=${points.lastX}, points.firstX=${points.firstX}") - //println("points.lastY=${points.lastY}, points.firstY=${points.firstY}") - if (!includeLastPoint && it.last.isAlmostEquals(it.first)) { - (it as PointArrayList).removeAt(it.size - 1) - //println("REMOVED LAST POINT!") - } - if (reversed) { - (it as PointArrayList).reverse() - } -}] +inline operator fun KMutableProperty0.get(path: VectorPath, includeLastPoint: Boolean = path.isLastCommandClose, reversed: Boolean = false): V2 = + this[path.getCurves().getEquidistantPoints().also { + //println("points.lastX=${points.lastX}, points.firstX=${points.firstX}") + //println("points.lastY=${points.lastY}, points.firstY=${points.firstY}") + if (!includeLastPoint && it.last.isAlmostEquals(it.first)) { + (it as PointArrayList).removeAt(it.size - 1) + //println("REMOVED LAST POINT!") + } + if (reversed) { + (it as PointArrayList).reverse() + } + }] @JvmName("getIPoint") @Deprecated("") -inline operator fun KMutableProperty0.get(path: VectorPath, includeLastPoint: Boolean = path.isLastCommandClose, reversed: Boolean = false): V2 = this[path.getCurves().getEquidistantPoints().also { +inline operator fun KMutableProperty0.get( + path: VectorPath, + includeLastPoint: Boolean = path.isLastCommandClose, + reversed: Boolean = false +): V2 = this[path.getCurves().getEquidistantPoints().also { //println("points.lastX=${points.lastX}, points.firstX=${points.firstX}") //println("points.lastY=${points.lastY}, points.firstY=${points.firstY}") if (!includeLastPoint && it.last.isAlmostEquals(it.first)) { @@ -209,16 +244,26 @@ inline operator fun KMutableProperty0.get(range: PointList): V2 { ) } -@JvmName("getFloat") inline operator fun KMutableProperty0.get(end: Float) = V2(this, this.get(), end, ::_interpolateFloat, includeStart = false) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(end: Int) = get(end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(end: Double) = get(end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(end: Long) = get(end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(end: Number) = get(end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(initial: Float, end: Float) = V2(this, initial, end, ::_interpolateFloat, true) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(initial: Int, end: Int) = get(initial.toFloat(), end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(initial: Double, end: Double) = get(initial.toFloat(), end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(initial: Long, end: Long) = get(initial.toFloat(), end.toFloat()) -@JvmName("getFloat") inline operator fun KMutableProperty0.get(initial: Number, end: Number) = get(initial.toFloat(), end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(end: Float) = V2(this, this.get(), end, ::_interpolateFloat, includeStart = false) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(end: Int) = get(end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(end: Double) = get(end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(end: Long) = get(end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(end: Number) = get(end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(initial: Float, end: Float) = V2(this, initial, end, ::_interpolateFloat, true) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(initial: Int, end: Int) = get(initial.toFloat(), end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(initial: Double, end: Double) = get(initial.toFloat(), end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(initial: Long, end: Long) = get(initial.toFloat(), end.toFloat()) +@JvmName("getFloat") +inline operator fun KMutableProperty0.get(initial: Number, end: Number) = get(initial.toFloat(), end.toFloat()) inline operator fun KMutableProperty0.get(end: Ratio) = V2(this, this.get(), end, ::_interpolateRatio, includeStart = false) inline operator fun KMutableProperty0.get(initial: Ratio, end: Ratio) = V2(this, initial, end, ::_interpolateRatio, true) @@ -245,14 +290,14 @@ inline operator fun KMutableProperty0.get(initial: Angle, end: Angle) = V fun V2.denormalized(): V2 = this.copy(interpolator = ::_interpolateAngleDenormalized) -inline operator fun KMutableProperty0.get(end: TimeSpan) = V2(this, this.get(), end, ::_interpolateTimeSpan, includeStart = false) -inline operator fun KMutableProperty0.get(initial: TimeSpan, end: TimeSpan) = V2(this, initial, end, ::_interpolateTimeSpan, includeStart = true) +inline operator fun KMutableProperty0.get(end: Duration) = V2(this, this.get(), end, ::_interpolateTimeSpan, includeStart = false) +inline operator fun KMutableProperty0.get(initial: Duration, end: Duration) = V2(this, initial, end, ::_interpolateTimeSpan, includeStart = true) fun V2.clamped(): V2 = copy(interpolator = { ratio, l, r -> this.interpolator(ratio.clamped, l, r) }) fun V2.easing(easing: Easing): V2 = this.copy(interpolator = { ratio, a, b -> this.interpolator(easing(ratio.toDouble()).toRatio(), a, b) }) -inline fun V2.delay(startTime: TimeSpan) = this.copy(startTime = startTime) -inline fun V2.duration(duration: TimeSpan) = this.copy(duration = duration) +inline fun V2.delay(startTime: Duration) = this.copy(startTime = startTime) +inline fun V2.duration(duration: Duration) = this.copy(duration = duration) inline fun V2.linear() = this inline fun V2.smooth() = this.easing(Easing.SMOOTH) diff --git a/korge/src/korlibs/korge/ui/UIScrollable.kt b/korge/src/korlibs/korge/ui/UIScrollable.kt index 931ee92401..3c99a87db4 100644 --- a/korge/src/korlibs/korge/ui/UIScrollable.kt +++ b/korge/src/korlibs/korge/ui/UIScrollable.kt @@ -15,6 +15,7 @@ import korlibs.math.geom.* import korlibs.math.interpolation.* import korlibs.time.* import kotlin.math.* +import kotlin.time.* inline fun Container.uiScrollable( size: Size = Size(256, 256), @@ -35,27 +36,30 @@ open class UIScrollable(size: Size, cache: Boolean = true) : UIView(size, cache var scrollBarPos: Double by if (isHorizontal) view::x else view::y var viewPos: Double by if (isHorizontal) view::x else view::y - //get() = if (isHorizontal) view.x else view.y - //set(value) = if (isHorizontal) view.x = value else view.y = value + + //get() = if (isHorizontal) view.x else view.y + //set(value) = if (isHorizontal) view.x = value else view.y = value var viewScaledSize: Double by if (isHorizontal) view::scaledWidth else view::scaledHeight - //get() = if (isHorizontal) view.scaledWidth else view.scaledHeight - //set(value: Double) = if (isHorizontal) view.scaledWidth = value else view.scaledHeight = value + //get() = if (isHorizontal) view.scaledWidth else view.scaledHeight + //set(value: Double) = if (isHorizontal) view.scaledWidth = value else view.scaledHeight = value val scrollRatio: Double get() = size / totalSize val scrollbarSize: Double get() = size * scrollRatio - val scaledSize: Double get() = if (isHorizontal) view.scaledWidth else view.scaledHeight + val scaledSize: Double get() = if (isHorizontal) view.scaledWidth else view.scaledHeight var containerPos: Double by if (isHorizontal) container::x else container::y - //get() = if (isHorizontal) container.x else container.y - //set(value) { if (isHorizontal) container.x = value else container.y = value } + //get() = if (isHorizontal) container.x else container.y + //set(value) { if (isHorizontal) container.x = value else container.y = value } val overflowPixelsBegin: Double get() = if (isHorizontal) scrollable.overflowPixelsLeft else scrollable.overflowPixelsTop val overflowPixelsEnd: Double get() = if (isHorizontal) scrollable.overflowPixelsRight else scrollable.overflowPixelsBottom val onScrollPosChange = Signal() val size: Double get() = if (isHorizontal) scrollable.width else scrollable.height val shouldBeVisible get() = (size < totalSize) - val totalSize: Double get() = (container.getLocalBounds().let { if (isHorizontal) max(scrollable.width, it.right) else max(scrollable.height, it.bottom) }) - //.also { println("totalSize=$it") } + val totalSize: Double + get() = (container.getLocalBounds().let { if (isHorizontal) max(scrollable.width, it.right) else max(scrollable.height, it.bottom) }) + + //.also { println("totalSize=$it") } val scrollArea: Double get() = totalSize - size val positionEnd: Double get() = position + size var position: Double @@ -72,12 +76,15 @@ open class UIScrollable(size: Size, cache: Boolean = true) : UIView(size, cache } } - @KorgeInternal fun scrollBarPositionToScrollTopLeft(pos: Double): Double { + @KorgeInternal + fun scrollBarPositionToScrollTopLeft(pos: Double): Double { val d = size - scaledSize if (d == 0.0) return 0.0 return (pos / d) * scrollArea } - @KorgeInternal fun scrollTopLeftToScrollBarPosition(pos: Double): Double { + + @KorgeInternal + fun scrollTopLeftToScrollBarPosition(pos: Double): Double { val d = scrollArea if (d == 0.0) return 0.0 return (pos / d) * (size - scaledSize) @@ -86,6 +93,7 @@ open class UIScrollable(size: Size, cache: Boolean = true) : UIView(size, cache fun ensurePositionIsVisible(position: Double, anchor: Double = 0.5) { ensureRangeIsVisible(position, position, anchor) } + fun ensureRangeIsVisible(start: Double, end: Double, anchor: Double = 0.5) { if (start !in this.position..this.positionEnd || end !in this.position..this.positionEnd) { this.position = (start - size * anchor).clamp(0.0, scrollArea) @@ -122,7 +130,6 @@ open class UIScrollable(size: Size, cache: Boolean = true) : UIView(size, cache var scrollLeft: Double by horizontal::position var scrollLeftRatio: Double by horizontal::positionRatio - // VERTICAL SCROLLBAR val onScrollTopChange: Signal get() = vertical.onScrollPosChange val scrollHeight: Double get() = vertical.totalSize @@ -131,6 +138,7 @@ open class UIScrollable(size: Size, cache: Boolean = true) : UIView(size, cache @ViewProperty var frictionRate = 0.75 + @ViewProperty var overflowRate = 0.1 val overflowPixelsVertical: Double get() = height * overflowRate @@ -139,18 +147,25 @@ open class UIScrollable(size: Size, cache: Boolean = true) : UIView(size, cache val overflowPixelsBottom: Double get() = overflowPixelsVertical val overflowPixelsLeft: Double get() = overflowPixelsHorizontal val overflowPixelsRight: Double get() = overflowPixelsHorizontal + @ViewProperty var containerX: Double by container::x + @ViewProperty var containerY: Double by container::y + @ViewProperty - var timeScrollBar: TimeSpan = 0.seconds + var timeScrollBar: Duration = 0.seconds + @ViewProperty var autohideScrollBar = false + @ViewProperty var scrollBarAlpha = 0.75 + @ViewProperty var backgroundColor: RGBA = Colors["#161a1d"] + @ViewProperty var mobileBehaviour = true diff --git a/korge/src/korlibs/korge/ui/UITooltipContainer.kt b/korge/src/korlibs/korge/ui/UITooltipContainer.kt index a162bba593..9764a12bff 100644 --- a/korge/src/korlibs/korge/ui/UITooltipContainer.kt +++ b/korge/src/korlibs/korge/ui/UITooltipContainer.kt @@ -12,6 +12,7 @@ import korlibs.image.color.* import korlibs.image.font.* import korlibs.io.lang.* import korlibs.math.geom.* +import kotlin.time.* @KorgeExperimental inline fun Container.uiTooltipContainer( @@ -26,15 +27,24 @@ class UITooltipContainer() : Container() { visible = false } - var tooltipFont: Font ; get() = tooltip.styles.textFont ; set(value) { tooltip.styles.textFont = value } - var tooltipColor: RGBA ; get() = tooltip.styles.textColor ; set(value) { tooltip.styles.textColor = value } - var tooltipBackgroundColor: RGBA ; get() = tooltip.bgcolor ; set(value) { tooltip.bgcolor = value } - var showTime: TimeSpan = 0.4.seconds - var appearAnimationTime: TimeSpan = 0.2.seconds + var tooltipFont: Font; get() = tooltip.styles.textFont; + set(value) { + tooltip.styles.textFont = value + } + var tooltipColor: RGBA; get() = tooltip.styles.textColor; + set(value) { + tooltip.styles.textColor = value + } + var tooltipBackgroundColor: RGBA; get() = tooltip.bgcolor; + set(value) { + tooltip.bgcolor = value + } + var showTime: Duration = 0.4.seconds + var appearAnimationTime: Duration = 0.2.seconds var tooltipOffsetX: Double = 0.0 var tooltipOffsetY: Double = 4.0 - private var visibleTimer: Closeable? = null + private var visibleTimer: AutoCloseable? = null fun disappear() { tooltip.visible = false diff --git a/korge/src/korlibs/korge/ui/UITooltipContainerMediatorNew.kt b/korge/src/korlibs/korge/ui/UITooltipContainerMediatorNew.kt index 334d5d8ff9..c2327c1720 100644 --- a/korge/src/korlibs/korge/ui/UITooltipContainerMediatorNew.kt +++ b/korge/src/korlibs/korge/ui/UITooltipContainerMediatorNew.kt @@ -30,7 +30,7 @@ val Container.closestUITooltipContainerMediatorNew: UITooltipContainerMediatorNe } @KorgeExperimental -class UITooltipContainerMediatorNew(val container: Container) : Closeable { +class UITooltipContainerMediatorNew(val container: Container) : AutoCloseable { class Tooltip(val mediator: UITooltipContainerMediatorNew, track: View, textData: RichTextData) : UIView(size = computeSize(textData)) { companion object { fun computeSize(textData: RichTextData): Size { diff --git a/korge/src/korlibs/korge/view/CachedContainer.kt b/korge/src/korlibs/korge/view/CachedContainer.kt index 2f06074683..e6c920597a 100644 --- a/korge/src/korlibs/korge/view/CachedContainer.kt +++ b/korge/src/korlibs/korge/view/CachedContainer.kt @@ -55,7 +55,7 @@ open class CachedContainer( } } - inner class CacheTexture(val ctx: RenderContext) : Closeable { + inner class CacheTexture(val ctx: RenderContext) : AutoCloseable { val rb = ctx.unsafeAllocateFrameBuffer(16, 16, onlyThisFrame = false) val texBase = TextureBase(rb.tex, 16, 16) var tex = Texture(texBase) diff --git a/korge/src/korlibs/korge/view/Camera.kt b/korge/src/korlibs/korge/view/Camera.kt index ec2f9caed2..e1b9f9a69c 100644 --- a/korge/src/korlibs/korge/view/Camera.kt +++ b/korge/src/korlibs/korge/view/Camera.kt @@ -5,6 +5,7 @@ import korlibs.korge.tween.* import korlibs.math.geom.* import korlibs.math.interpolation.* import korlibs.time.* +import kotlin.time.* /** * Creates a new [Camera] and attaches to [this] [Container]. The [callback] argument is called with the [Camera] injected as this to be able to configure the camera. @@ -53,14 +54,14 @@ open class Camera : Container(), View.Reference { fun setTo(view: View?) { this.localMatrix = getLocalMatrixFittingView(view) } fun setTo(rect: Rectangle) { this.localMatrix = getLocalMatrixFittingGlobalRect(rect) } - suspend fun tweenTo(view: View?, vararg vs: V2<*>, time: TimeSpan, easing: Easing = Easing.LINEAR) = this.tween( + suspend fun tweenTo(view: View?, vararg vs: V2<*>, time: Duration, easing: Easing = Easing.LINEAR) = this.tween( this::localMatrix[this.localMatrix, getLocalMatrixFittingView(view)], *vs, time = time, easing = easing ) - suspend fun tweenTo(rect: Rectangle, vararg vs: V2<*>, time: TimeSpan, easing: Easing = Easing.LINEAR) = this.tween( + suspend fun tweenTo(rect: Rectangle, vararg vs: V2<*>, time: Duration, easing: Easing = Easing.LINEAR) = this.tween( this::localMatrix[this.localMatrix, getLocalMatrixFittingGlobalRect(rect)], *vs, time = time, diff --git a/korge/src/korlibs/korge/view/FpsOverlay.kt b/korge/src/korlibs/korge/view/FpsOverlay.kt index acbc08deac..125c9522a6 100644 --- a/korge/src/korlibs/korge/view/FpsOverlay.kt +++ b/korge/src/korlibs/korge/view/FpsOverlay.kt @@ -8,6 +8,7 @@ import korlibs.korge.render.* import korlibs.math.* import korlibs.platform.* import korlibs.time.* +import kotlin.time.* @OptIn(KorgeInternal::class) internal fun ViewsContainer.installFpsDebugOverlay() { @@ -161,18 +162,19 @@ private class TimeSlidingWindow(val capacity: Int) { val size get() = deque.size - val avg: TimeSpan get() = if (deque.size == 0) 0.seconds else (totalMicroseconds.toDouble() / deque.size).microseconds + val avg: Duration get() = if (deque.size == 0) 0.seconds else (totalMicroseconds.toDouble() / deque.size).microseconds + // @TODO: Can we compute this incrementally? - val min: TimeSpan get() = deque.minOrNull()?.microseconds ?: 1.microseconds - val max: TimeSpan get() = deque.maxOrNull()?.microseconds ?: 1.microseconds + val min: Duration get() = deque.minOrNull()?.microseconds ?: 1.microseconds + val max: Duration get() = deque.maxOrNull()?.microseconds ?: 1.microseconds val avgFps: Float get() = (1.seconds / avg).toFloat() val minFps: Float get() = (1.seconds / max).toFloat() val maxFps: Float get() = (1.seconds / min).toFloat() - operator fun get(index: Int): TimeSpan = deque[index].microseconds + operator fun get(index: Int): Duration = deque[index].microseconds - fun add(value: TimeSpan) { + fun add(value: Duration) { val intValue = value.microsecondsInt deque.add(intValue) totalMicroseconds += intValue diff --git a/korge/src/korlibs/korge/view/Sprite.kt b/korge/src/korlibs/korge/view/Sprite.kt index 74444e0a9e..eee3ab4a20 100644 --- a/korge/src/korlibs/korge/view/Sprite.kt +++ b/korge/src/korlibs/korge/view/Sprite.kt @@ -6,6 +6,7 @@ import korlibs.image.bitmap.* import korlibs.io.async.Signal import korlibs.math.geom.* import korlibs.math.geom.vector.VectorPath +import kotlin.time.* inline fun Container.sprite( initialAnimation: SpriteAnimation, anchor: Anchor = Anchor.TOP_LEFT, callback: @ViewDslMarker Sprite.() -> Unit = {} @@ -44,7 +45,7 @@ open class Sprite( smoothing: Boolean = true ) : BaseImage(bitmap, anchor, hitShape, smoothing) { constructor( - bitmap : Bitmap, + bitmap: Bitmap, anchor: Anchor = Anchor.TOP_LEFT, hitShape: VectorPath? = null, smoothing: Boolean = true @@ -80,34 +81,34 @@ open class Sprite( private var _onAnimationStarted: Signal? = null private var _onFrameChanged: Signal? = null - val onAnimationCompleted : Signal - get(){ + val onAnimationCompleted: Signal + get() { if (_onAnimationCompleted == null) _onAnimationCompleted = Signal() return _onAnimationCompleted!! } - val onAnimationStopped : Signal + val onAnimationStopped: Signal get() { if (_onAnimationStopped == null) _onAnimationStopped = Signal() return _onAnimationStopped!! } - val onAnimationStarted : Signal + val onAnimationStarted: Signal get() { if (_onAnimationStarted == null) _onAnimationStarted = Signal() return _onAnimationStarted!! } - val onFrameChanged : Signal + val onFrameChanged: Signal get() { if (_onFrameChanged == null) _onFrameChanged = Signal() return _onFrameChanged!! } - var spriteDisplayTime: TimeSpan = 50.milliseconds + var spriteDisplayTime: Duration = 50.milliseconds private var animationLooped = false - private var lastAnimationFrameTime: TimeSpan = 0.milliseconds - private var animationRemainingDuration: TimeSpan = 0.milliseconds + private var lastAnimationFrameTime: Duration = 0.milliseconds + private var animationRemainingDuration: Duration = 0.milliseconds set(value) { if (value <= 0.milliseconds && animationType == AnimationType.DURATION) { stopAnimation() @@ -135,7 +136,7 @@ open class Sprite( } } - private fun getDefaultTime(spriteAnimation: SpriteAnimation?): TimeSpan = when { + private fun getDefaultTime(spriteAnimation: SpriteAnimation?): Duration = when { spriteAnimation != null && spriteAnimation.defaultTimePerFrame != TimeSpan.NIL -> spriteAnimation.defaultTimePerFrame else -> spriteDisplayTime } @@ -143,7 +144,7 @@ open class Sprite( fun playAnimation( times: Int = 1, spriteAnimation: SpriteAnimation? = currentAnimation, - spriteDisplayTime: TimeSpan = getDefaultTime(spriteAnimation), + spriteDisplayTime: Duration = getDefaultTime(spriteAnimation), startFrame: Int = -1, endFrame: Int = 0, reversed: Boolean = false @@ -159,7 +160,7 @@ open class Sprite( fun playAnimation( spriteAnimation: SpriteAnimation? = currentAnimation, - spriteDisplayTime: TimeSpan = getDefaultTime(spriteAnimation), + spriteDisplayTime: Duration = getDefaultTime(spriteAnimation), startFrame: Int = -1, endFrame: Int = 0, reversed: Boolean = false @@ -174,9 +175,9 @@ open class Sprite( ) fun playAnimationForDuration( - duration: TimeSpan, + duration: Duration, spriteAnimation: SpriteAnimation? = currentAnimation, - spriteDisplayTime: TimeSpan = getDefaultTime(spriteAnimation), + spriteDisplayTime: Duration = getDefaultTime(spriteAnimation), startFrame: Int = -1, reversed: Boolean = false ) = updateCurrentAnimation( @@ -190,7 +191,7 @@ open class Sprite( fun playAnimationLooped( spriteAnimation: SpriteAnimation? = currentAnimation, - spriteDisplayTime: TimeSpan = getDefaultTime(spriteAnimation), + spriteDisplayTime: Duration = getDefaultTime(spriteAnimation), startFrame: Int = -1, reversed: Boolean = false ) = updateCurrentAnimation( @@ -207,7 +208,7 @@ open class Sprite( triggerEvent(_onAnimationStopped) } - private fun nextSprite(frameTime: TimeSpan) { + private fun nextSprite(frameTime: Duration) { lastAnimationFrameTime += frameTime if (lastAnimationFrameTime + frameTime >= this.spriteDisplayTime) { when (animationType) { @@ -216,11 +217,12 @@ open class Sprite( animationNumberOfFramesRequested-- } } + AnimationType.DURATION -> { animationRemainingDuration -= lastAnimationFrameTime } - AnimationType.LOOPED -> { + AnimationType.LOOPED -> { } } if (reversed) --currentSpriteIndex else ++currentSpriteIndex @@ -230,16 +232,17 @@ open class Sprite( } } - val totalFrames: Int get() { - val ca = currentAnimation ?: return 1 - return ca.size - } + val totalFrames: Int + get() { + val ca = currentAnimation ?: return 1 + return ca.size + } private fun updateCurrentAnimation( spriteAnimation: SpriteAnimation?, - spriteDisplayTime: TimeSpan = getDefaultTime(spriteAnimation), + spriteDisplayTime: Duration = getDefaultTime(spriteAnimation), animationCyclesRequested: Int = 1, - duration: TimeSpan = 0.milliseconds, + duration: Duration = 0.milliseconds, startFrame: Int = 0, endFrame: Int = 0, looped: Boolean = false, @@ -258,8 +261,8 @@ open class Sprite( val endFrame = endFrame umod totalFrames currentAnimation?.let { val count = when { - startFrame > endFrame -> (if (reversed) startFrame - endFrame else it.spriteStackSize-(startFrame - endFrame)) - endFrame > startFrame -> (if (reversed) (startFrame - endFrame) umod it.spriteStackSize else endFrame-startFrame) + startFrame > endFrame -> (if (reversed) startFrame - endFrame else it.spriteStackSize - (startFrame - endFrame)) + endFrame > startFrame -> (if (reversed) (startFrame - endFrame) umod it.spriteStackSize else endFrame - startFrame) else -> 0 } val requestedFrames = count + (animationCyclesRequested * it.spriteStackSize) diff --git a/korge/src/korlibs/korge/view/SpriteAnimation.kt b/korge/src/korlibs/korge/view/SpriteAnimation.kt index faa8990c2f..c9b95ed117 100644 --- a/korge/src/korlibs/korge/view/SpriteAnimation.kt +++ b/korge/src/korlibs/korge/view/SpriteAnimation.kt @@ -5,10 +5,11 @@ import korlibs.image.atlas.* import korlibs.image.bitmap.* import korlibs.math.* import korlibs.time.* +import kotlin.time.* class SpriteAnimation constructor( val sprites: List, - val defaultTimePerFrame: TimeSpan = TimeSpan.NIL + val defaultTimePerFrame: Duration = TimeSpan.NIL ) : Collection by sprites { companion object { operator fun invoke( @@ -64,8 +65,8 @@ class SpriteAnimation constructor( operator fun get(index: Int) = getSprite(index) } -fun Atlas.getSpriteAnimation(prefix: String = "", defaultTimePerFrame: TimeSpan = TimeSpan.NIL): SpriteAnimation = +fun Atlas.getSpriteAnimation(prefix: String = "", defaultTimePerFrame: Duration = TimeSpan.NIL): SpriteAnimation = SpriteAnimation(this.entries.filter { it.filename.startsWith(prefix) }.map { it.slice }.toFastList(), defaultTimePerFrame) -fun Atlas.getSpriteAnimation(regex: Regex, defaultTimePerFrame: TimeSpan = TimeSpan.NIL): SpriteAnimation = +fun Atlas.getSpriteAnimation(regex: Regex, defaultTimePerFrame: Duration = TimeSpan.NIL): SpriteAnimation = SpriteAnimation(this.entries.filter { regex.matches(it.filename) }.map { it.slice }.toFastList(), defaultTimePerFrame) diff --git a/korge/src/korlibs/korge/view/View.kt b/korge/src/korlibs/korge/view/View.kt index 1a3f6af001..a7de2c373b 100644 --- a/korge/src/korlibs/korge/view/View.kt +++ b/korge/src/korlibs/korge/view/View.kt @@ -27,6 +27,7 @@ import korlibs.number.* import korlibs.time.* import kotlin.jvm.* import kotlin.math.* +import kotlin.time.* /** * KorGE includes a DOM-based tree of views that makes a chain of affine transforms starting with the [Stage], that is the root node. @@ -1471,13 +1472,13 @@ fun View?.commonAncestor(ancestor: View?): View? = View.commonAncestor(this, anc fun View.replaceWith(view: View): Boolean = this.parent?.replaceChild(this, view) ?: false /** Adds a block that will be executed per frame to this view. As parameter the block will receive a [TimeSpan] with the time elapsed since the previous frame. */ -fun T.addUpdater(first: Boolean = true, firstTime: TimeSpan = TimeSpan.ZERO, updatable: T.(dt: TimeSpan) -> Unit): CloseableCancellable { +fun T.addUpdater(first: Boolean = true, firstTime: Duration = TimeSpan.ZERO, updatable: T.(dt: Duration) -> Unit): CloseableCancellable { if (first) updatable(this, firstTime) return onEvent(UpdateEvent) { updatable(this, it.deltaTime * this.globalSpeed) } } -fun T.addUpdater(updatable: T.(dt: TimeSpan) -> Unit): CloseableCancellable = addUpdater(true, updatable = updatable) +fun T.addUpdater(updatable: T.(dt: Duration) -> Unit): CloseableCancellable = addUpdater(true, updatable = updatable) -fun T.addUpdaterWithViews(updatable: T.(views: Views, dt: TimeSpan) -> Unit): CloseableCancellable = onEvent(ViewsUpdateEvent) { +fun T.addUpdaterWithViews(updatable: T.(views: Views, dt: Duration) -> Unit): CloseableCancellable = onEvent(ViewsUpdateEvent) { updatable(this@addUpdaterWithViews, it.views, it.delta * this.globalSpeed) } @@ -1495,7 +1496,7 @@ fun T.deferWithViews(views: Views? = null, tryImmediate: Boolean = tr return this } -fun T.addOptFixedUpdater(time: TimeSpan = TimeSpan.NIL, updatable: T.(dt: TimeSpan) -> Unit): CloseableCancellable = when (time) { +fun T.addOptFixedUpdater(time: Duration = TimeSpan.NIL, updatable: T.(dt: Duration) -> Unit): CloseableCancellable = when (time) { TimeSpan.NIL -> addUpdater(updatable) else -> addFixedUpdater(time) { updatable(time) } } @@ -1513,7 +1514,7 @@ fun T.addFixedUpdater( * To avoid executing too many blocks, when there is a long pause, [limitCallsPerFrame] limits the number of times the block can be executed in a single frame. */ fun T.addFixedUpdater( - time: TimeSpan, + time: Duration, first: Boolean = true, limitCallsPerFrame: Int = 16, updatable: T.() -> Unit @@ -1543,10 +1544,10 @@ fun T.addFixedUpdater( } @Deprecated("Use addUpdater instead", ReplaceWith("addUpdater(updatable)")) -inline fun T.onFrame(noinline updatable: T.(dt: TimeSpan) -> Unit): Cancellable = addUpdater(updatable) +inline fun T.onFrame(noinline updatable: T.(dt: Duration) -> Unit): Cancellable = addUpdater(updatable) fun T.onNextFrame(block: T.(views: Views) -> Unit): CloseableCancellable { - var closeable: Closeable? = null + var closeable: AutoCloseable? = null closeable = addUpdaterWithViews { views, _ -> block(views) closeable?.close() diff --git a/korge/src/korlibs/korge/view/Views.kt b/korge/src/korlibs/korge/view/Views.kt index 29346cff47..3025fdce4c 100644 --- a/korge/src/korlibs/korge/view/Views.kt +++ b/korge/src/korlibs/korge/view/Views.kt @@ -31,6 +31,7 @@ import korlibs.time.* import kotlinx.coroutines.* import kotlin.collections.set import kotlin.coroutines.* +import kotlin.time.* //@Singleton /** @@ -54,9 +55,9 @@ class Views( ) : BaseEventListener(), Extra by Extra.Mixin(), CoroutineScope, ViewsContainer, - BoundsProvider by bp, + BoundsProvider by bp, DialogInterfaceProvider by gameWindow, - Closeable, + AutoCloseable, ResourcesContainer, InvalidateNotifier, DeviceDimensionsProvider by gameWindow @@ -309,7 +310,7 @@ class Views( } fun frameUpdateAndRender( - fixedSizeStep: TimeSpan = TimeSpan.NIL, + fixedSizeStep: Duration = TimeSpan.NIL, forceRender: Boolean = false, doUpdate: Boolean = true, doRender: Boolean = true, @@ -346,7 +347,7 @@ class Views( private val eventResults = EventResult() - fun update(elapsed: TimeSpan) { + fun update(elapsed: Duration) { //println(this) //println("Update: $elapsed") input.startFrame(elapsed) @@ -558,7 +559,7 @@ private fun getAllDescendantViewsBase(view: View, out: FastArrayList, reve } @OptIn(KorgeInternal::class) -fun View.updateSingleView(delta: TimeSpan, tempUpdate: UpdateEvent = UpdateEvent()) { +fun View.updateSingleView(delta: Duration, tempUpdate: UpdateEvent = UpdateEvent()) { dispatch(tempUpdate.also { it.deltaTime = delta }) } @@ -574,7 +575,7 @@ fun View.updateSingleView(delta: TimeSpan, tempUpdate: UpdateEvent = UpdateEvent @OptIn(KorgeInternal::class) fun View.updateSingleViewWithViewsAll( views: Views, - delta: TimeSpan, + delta: Duration, ) { dispatch(views.updateEvent.also { it.deltaTime = delta }) dispatch(views.viewsUpdateEvent.also { it.delta = delta }) @@ -664,7 +665,7 @@ fun BoundsProvider.setBoundsInfo( suspend fun views(): Views = injector().get() -class UpdateEvent(var deltaTime: TimeSpan = TimeSpan.ZERO) : Event(), TEvent { +class UpdateEvent(var deltaTime: Duration = TimeSpan.ZERO) : Event(), TEvent { companion object : EventType override val type: EventType get() = UpdateEvent @@ -675,7 +676,7 @@ class UpdateEvent(var deltaTime: TimeSpan = TimeSpan.ZERO) : Event(), TEvent { +class ViewsUpdateEvent(val views: Views, var delta: Duration = TimeSpan.ZERO) : Event(), TEvent { companion object : EventType override val type: EventType get() = ViewsUpdateEvent diff --git a/korge/src/korlibs/korge/view/camera/Camera.kt b/korge/src/korlibs/korge/view/camera/Camera.kt index 6bdb2fe789..97a9ab0ac1 100644 --- a/korge/src/korlibs/korge/view/camera/Camera.kt +++ b/korge/src/korlibs/korge/view/camera/Camera.kt @@ -7,6 +7,7 @@ import korlibs.math.geom.* import korlibs.math.interpolation.* import korlibs.time.* import kotlin.math.* +import kotlin.time.* @Deprecated("") inline fun Container.cameraContainer( @@ -149,7 +150,7 @@ class CameraContainer( sync() } - fun setTargetCamera(camera: Camera, time: TimeSpan = 1.seconds, easing: Easing = Easing.LINEAR) { + fun setTargetCamera(camera: Camera, time: Duration = 1.seconds, easing: Easing = Easing.LINEAR) { elapsedTime = 0.seconds this.transitionTime = time this.easing = easing @@ -158,7 +159,7 @@ class CameraContainer( targetCamera.copyFrom(camera) } - suspend fun tweenCamera(camera: Camera, time: TimeSpan = 1.seconds, easing: Easing = Easing.LINEAR) { + suspend fun tweenCamera(camera: Camera, time: Duration = 1.seconds, easing: Easing = Easing.LINEAR) { setTargetCamera(camera, time, easing) onCompletedTransition.waitOne() } diff --git a/korge/src/korlibs/korge/view/filter/FlagFilter.kt b/korge/src/korlibs/korge/view/filter/FlagFilter.kt index c78935be5e..6dfa45bddb 100644 --- a/korge/src/korlibs/korge/view/filter/FlagFilter.kt +++ b/korge/src/korlibs/korge/view/filter/FlagFilter.kt @@ -8,6 +8,7 @@ import korlibs.math.* import korlibs.math.geom.* import korlibs.time.* import kotlin.math.* +import kotlin.time.* /** * A Flag [Filter] that distorts the texture using increasing waves to the right, keeping the left-most vertical static, @@ -22,7 +23,7 @@ class FlagFilter( amplitude: Double = 80.0, crestCount: Double = 5.0, cyclesPerSecond: Double = 2.0, - time: TimeSpan = 0.seconds + time: Duration = 0.seconds ) : ShaderFilter() { object FlagUB : UniformBlock(fixedLocation = 5) { val u_amplitude by float() @@ -36,7 +37,7 @@ class FlagFilter( amplitude: Number = 80.0, crestCount: Number = 5.0, cyclesPerSecond: Number = 2.0, - time: TimeSpan = 0.seconds + time: Duration = 0.seconds ): FlagFilter = FlagFilter(amplitude.toDouble(), crestCount.toDouble(), cyclesPerSecond.toDouble(), time) override val fragment: FragmentShader = FragmentShaderDefault { @@ -62,7 +63,7 @@ class FlagFilter( var cyclesPerSecond: Double = cyclesPerSecond.toDouble() /** The elapsed time for the animation */ - var time: TimeSpan = time + var time: Duration = time override val programProvider: ProgramProvider get() = FlagFilter diff --git a/korge/src/korlibs/korge/view/filter/SwizzleColorFilter.kt b/korge/src/korlibs/korge/view/filter/SwizzleColorFilter.kt index 3d8132d29d..297aa9f10d 100644 --- a/korge/src/korlibs/korge/view/filter/SwizzleColorFilter.kt +++ b/korge/src/korlibs/korge/view/filter/SwizzleColorFilter.kt @@ -1,6 +1,6 @@ package korlibs.korge.view.filter -import korlibs.datastructure.lock.* +import korlibs.concurrent.lock.* import korlibs.graphics.shader.* import korlibs.korge.view.property.* diff --git a/korge/src/korlibs/korge/view/filter/WaveFilter.kt b/korge/src/korlibs/korge/view/filter/WaveFilter.kt index 96053ce65c..3655895edc 100644 --- a/korge/src/korlibs/korge/view/filter/WaveFilter.kt +++ b/korge/src/korlibs/korge/view/filter/WaveFilter.kt @@ -9,6 +9,7 @@ import korlibs.math.geom.* import korlibs.memory.* import korlibs.time.* import kotlin.math.* +import kotlin.time.* /** * A Wave [Filter] that distorts the texture using waves. @@ -22,7 +23,7 @@ class WaveFilter( amplitude: Vector2D = Vector2D(10, 10), crestDistance: Vector2D = Vector2D(16, 16), cyclesPerSecond: Vector2D = Vector2D(1, 1), - time: TimeSpan = 0.seconds + time: Duration = 0.seconds ) : ShaderFilter() { /** Maximum amplitude of the wave on the X,Y axis */ @ViewProperty diff --git a/korge/src/korlibs/render/GameWindow.kt b/korge/src/korlibs/render/GameWindow.kt index 33a8f865a3..6bc139d70b 100644 --- a/korge/src/korlibs/render/GameWindow.kt +++ b/korge/src/korlibs/render/GameWindow.kt @@ -1,8 +1,7 @@ package korlibs.render +import korlibs.concurrent.lock.* import korlibs.datastructure.* -import korlibs.datastructure.closeable.* -import korlibs.datastructure.lock.* import korlibs.event.* import korlibs.graphics.* import korlibs.graphics.log.* @@ -18,10 +17,12 @@ import korlibs.math.geom.* import korlibs.memory.* import korlibs.render.GameWindow.Quality.* import korlibs.time.* +import korlibs.time.measureTime import kotlinx.coroutines.* import kotlin.coroutines.* import kotlin.native.concurrent.* import kotlin.properties.* +import kotlin.time.* @ThreadLocal var GLOBAL_CHECK_GL = false @@ -101,8 +102,7 @@ open class GameWindow : CoroutineContext.Element, AGWindow, GameWindowConfig, - Extra by Extra.Mixin() -{ + Extra by Extra.Mixin() { open val androidContextAny: Any? get() = null sealed interface ICursor @@ -215,6 +215,7 @@ open class GameWindow : open var alwaysOnTop: Boolean = false override val key: CoroutineContext.Key<*> get() = CoroutineKey + companion object CoroutineKey : CoroutineContext.Key { const val DEFAULT_PREFERRED_FPS = 60 val logger = Logger("GameWindow") @@ -251,23 +252,23 @@ open class GameWindow : protected val dropFileEvent = DropFileEvent() /** Happens on the updater thread */ - fun onUpdateEvent(block: (UpdateEvent) -> Unit): Closeable { + fun onUpdateEvent(block: (UpdateEvent) -> Unit): AutoCloseable { return onEvent(UpdateEvent, block) } /** Happens on the rendering thread */ - fun onRenderEvent(block: (RenderEvent) -> Unit): Closeable { + fun onRenderEvent(block: (RenderEvent) -> Unit): AutoCloseable { return onEvent(RenderEvent, block) } - val counterTimePerFrame: TimeSpan get() = (1_000_000.0 / fps).microseconds - val timePerFrame: TimeSpan get() = counterTimePerFrame + val counterTimePerFrame: Duration get() = (1_000_000.0 / fps).microseconds + val timePerFrame: Duration get() = counterTimePerFrame open fun computeDisplayRefreshRate(): Int { return 60 } - open fun registerTime(name: String, time: TimeSpan) { + open fun registerTime(name: String, time: Duration) { //println("registerTime: $name=$time") } @@ -308,6 +309,7 @@ open class GameWindow : fun hide() { visible = false } + fun show() { visible = true } @@ -330,8 +332,10 @@ open class GameWindow : enum class Quality(override val level: Float) : korlibs.image.Quality { /** Will render to lower resolutions, ignoring devicePixelRatio on retina-like screens */ PERFORMANCE(0f), + /** Will render to higher resolutions, using devicePixelRatio on retina-like screens */ QUALITY(1f), + /** Will choose [PERFORMANCE] or [QUALITY] based on some heuristics */ AUTOMATIC(.5f); @@ -354,6 +358,7 @@ open class GameWindow : } open fun setSize(width: Int, height: Int): Unit = Unit + // Alias for close fun exit(exitCode: Int = 0): Unit = close(exitCode) @@ -401,7 +406,7 @@ open class GameWindow : onContinuousRenderModeUpdated?.invoke(new) } - fun frame(doUpdate: Boolean = true, doRender: Boolean = true, frameStartTime: TimeSpan = PerformanceCounter.reference): TimeSpan { + fun frame(doUpdate: Boolean = true, doRender: Boolean = true, frameStartTime: Duration = PerformanceCounter.reference): Duration { val startTime = PerformanceCounter.reference if (doRender) { renderTime = measureTime { @@ -492,8 +497,8 @@ open class GameWindow : surfaceHeight = height } - var gamePadTime: TimeSpan = TimeSpan.ZERO - fun frameUpdate(startTime: TimeSpan) { + var gamePadTime: Duration = TimeSpan.ZERO + fun frameUpdate(startTime: Duration) { gamePadTime = measureTime { updateGamepads() } @@ -502,7 +507,8 @@ open class GameWindow : val consumed = now - startTime //val remaining = (counterTimePerFrame - consumed) - 2.milliseconds // Do not push too much so give two extra milliseconds just in case //val timeForTasks = coroutineDispatcher.maxAllocatedTimeForTasksPerFrame ?: (remaining * 10) // We would be skipping up to 10 frames by default - val timeForTasks = coroutineDispatcher.maxAllocatedTimeForTasksPerFrame ?: (counterTimePerFrame * 10) // We would be skipping up to 10 frames by default + val timeForTasks = + coroutineDispatcher.maxAllocatedTimeForTasksPerFrame ?: (counterTimePerFrame * 10) // We would be skipping up to 10 frames by default val finalTimeForTasks = max(timeForTasks, 4.milliseconds) // Avoid having 0 milliseconds or even negative //println(" - frameUpdate: finalTimeForTasks=$finalTimeForTasks, startTime=$startTime, now=$now") coroutineDispatcher.executePending(finalTimeForTasks) @@ -515,7 +521,7 @@ open class GameWindow : open fun updateGamepads() { } - fun executePending(availableTime: TimeSpan) { + fun executePending(availableTime: Duration) { coroutineDispatcher.executePending(availableTime) } @@ -529,9 +535,11 @@ open class GameWindow : inline fun updateRenderLock(block: () -> Unit) { _updateRenderLock(block) } + fun dispatchUpdateEvent() { updateRenderLock { dispatch(updateEvent) } } + fun dispatchRenderEvent(update: Boolean = true, render: Boolean = true) { updateRenderLock { dispatch(renderEvent.reset { @@ -540,13 +548,16 @@ open class GameWindow : }) } } + fun dispatchNewRenderEvent() { dispatchRenderEvent(update = false, render = true) } + fun dispatchDropfileEvent(type: DropFileEvent.Type, files: List?) = dispatch(dropFileEvent.reset { this.type = type this.files = files }) + fun dispatchFullscreenEvent(fullscreen: Boolean) = dispatch(fullScreenEvent.reset { this.fullscreen = fullscreen }) fun dispatchReshapeEvent(x: Int, y: Int, width: Int, height: Int) { @@ -671,7 +682,6 @@ open class GameWindow : //} } - // @TODO: Is this used? fun entry(callback: suspend () -> Unit) { launch(coroutineDispatcher) { @@ -753,13 +763,13 @@ open class EventLoopGameWindow : GameWindow() { fun elapsedSinceLastRenderTime() = PerformanceCounter.reference - lastRenderTime inline fun render(doUpdate: Boolean, doRender: () -> Boolean = { true }) { - val frameStartTime: TimeSpan = PerformanceCounter.reference + val frameStartTime: Duration = PerformanceCounter.reference val mustRender = doRender() if (mustRender) renderInternal(doUpdate = doUpdate, frameStartTime = frameStartTime) } @PublishedApi - internal fun renderInternal(doUpdate: Boolean, frameStartTime: TimeSpan = PerformanceCounter.reference) { + internal fun renderInternal(doUpdate: Boolean, frameStartTime: Duration = PerformanceCounter.reference) { fixedTime = PerformanceCounter.reference doInitRender() @@ -790,7 +800,7 @@ open class EventLoopGameWindow : GameWindow() { } } - protected fun sleep(time: TimeSpan) { + protected fun sleep(time: Duration) { // Reimplement: Spinlock! val start = PerformanceCounter.reference while ((PerformanceCounter.reference - start) < time) { diff --git a/korge/src/korlibs/render/GameWindowCoroutineDispatcher.kt b/korge/src/korlibs/render/GameWindowCoroutineDispatcher.kt index 634838451a..ebdb22cf63 100644 --- a/korge/src/korlibs/render/GameWindowCoroutineDispatcher.kt +++ b/korge/src/korlibs/render/GameWindowCoroutineDispatcher.kt @@ -13,12 +13,12 @@ import kotlin.time.* @OptIn(InternalCoroutinesApi::class) class GameWindowCoroutineDispatcher( - var nowProvider: () -> TimeSpan = { PerformanceCounter.reference }, + var nowProvider: () -> Duration = { PerformanceCounter.reference }, var fast: Boolean = false, -) : CoroutineDispatcher(), Delay, Closeable { +) : CoroutineDispatcher(), Delay, AutoCloseable { override fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block) - class TimedTask(val time: TimeSpan, val continuation: CancellableContinuation?, val callback: Runnable?) { + class TimedTask(val time: Duration, val continuation: CancellableContinuation?, val callback: Runnable?) { var exception: Throwable? = null } @@ -54,7 +54,7 @@ class GameWindowCoroutineDispatcher( scheduleResumeAfterDelay(timeMillis.toDouble().milliseconds, continuation) } - fun scheduleResumeAfterDelay(time: TimeSpan, continuation: CancellableContinuation) { + fun scheduleResumeAfterDelay(time: Duration, continuation: CancellableContinuation) { val task = TimedTask(now() + time, continuation, null) continuation.invokeOnCancellation { task.exception = it @@ -76,9 +76,9 @@ class GameWindowCoroutineDispatcher( * despite time available in the frame. * When not set it uses the remaining available time in frame **/ - var maxAllocatedTimeForTasksPerFrame: TimeSpan? = null + var maxAllocatedTimeForTasksPerFrame: Duration? = null - fun executePending(availableTime: TimeSpan) { + fun executePending(availableTime: Duration) { try { val startTime = now() @@ -140,7 +140,7 @@ class GameWindowCoroutineDispatcher( val tooManyCallbacksLogger = Logger("Korgw.GameWindow.TooManyCallbacks") - fun informTooManyCallbacksToHandleInThisFrame(elapsedTime: TimeSpan, availableTime: TimeSpan, processedTimedTasks: Int, processedTasks: Int) { + fun informTooManyCallbacksToHandleInThisFrame(elapsedTime: Duration, availableTime: Duration, processedTimedTasks: Int, processedTasks: Int) { tooManyCallbacksLogger.warn { "Too many callbacks to handle in this frame elapsedTime=${elapsedTime.roundMilliseconds()}, availableTime=${availableTime.roundMilliseconds()} pending timedTasks=${timedTasks.size}, tasks=${tasks.size}, processedTimedTasks=$processedTimedTasks, processedTasks=$processedTasks" } } diff --git a/korge/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt b/korge/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt index d2a00d7072..f8cf3593b6 100644 --- a/korge/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt +++ b/korge/src/korlibs/render/SyncEventLoopCoroutineDispatcher.kt @@ -1,6 +1,5 @@ package korlibs.render -import korlibs.datastructure.closeable.Closeable import korlibs.datastructure.event.* import korlibs.io.lang.* import korlibs.time.* @@ -8,7 +7,7 @@ import kotlinx.coroutines.* import kotlin.coroutines.* @OptIn(InternalCoroutinesApi::class) -class SyncEventLoopCoroutineDispatcher(val eventLoop: SyncEventLoop) : CoroutineDispatcher(), Delay, Closeable { +class SyncEventLoopCoroutineDispatcher(val eventLoop: SyncEventLoop) : CoroutineDispatcher(), Delay, AutoCloseable { constructor(precise: Boolean = true, immediateRun: Boolean = false) : this(SyncEventLoop(precise, immediateRun)) override fun close() { diff --git a/korge/src@darwin/korlibs/korge/service/vibration/NativeVibration.kt b/korge/src@darwin/korlibs/korge/service/vibration/NativeVibration.kt index 53db251193..dac20b9f24 100644 --- a/korge/src@darwin/korlibs/korge/service/vibration/NativeVibration.kt +++ b/korge/src@darwin/korlibs/korge/service/vibration/NativeVibration.kt @@ -2,6 +2,7 @@ package korlibs.korge.service.vibration import korlibs.time.TimeSpan import korlibs.korge.view.Views +import kotlin.time.* actual class NativeVibration actual constructor(val views: Views) { /** @@ -9,7 +10,7 @@ actual class NativeVibration actual constructor(val views: Views) { * @param amplitudes list of intensities of the vibration. A `0.2` results in 20% vibration power. */ @ExperimentalUnsignedTypes - actual fun vibratePattern(timings: Array, amplitudes: Array) { + actual fun vibratePattern(timings: Array, amplitudes: Array) { } /** @@ -17,6 +18,6 @@ actual class NativeVibration actual constructor(val views: Views) { * @param amplitude percentage intensity of the vibration. A `0.2` results in 20% vibration power. */ @ExperimentalUnsignedTypes - actual fun vibrate(time: TimeSpan, amplitude: Double) { + actual fun vibrate(time: Duration, amplitude: Double) { } } diff --git a/korge/src@darwin/korlibs/render/DefaultGameWindowIos.kt b/korge/src@darwin/korlibs/render/DefaultGameWindowIos.kt index 46f1b2a674..ace56b8629 100644 --- a/korge/src@darwin/korlibs/render/DefaultGameWindowIos.kt +++ b/korge/src@darwin/korlibs/render/DefaultGameWindowIos.kt @@ -226,6 +226,12 @@ class MyGLKViewController( lastHeight = 0 } + private fun nativeCwdOrNull(): String? { + return kotlinx.cinterop.autoreleasepool { + platform.Foundation.NSBundle.mainBundle.resourcePath + } + } + override fun glkView(view: GLKView, drawInRect: CValue) { if (!initialized) { initialized = true diff --git a/korge/src@js/korlibs/datastructure/event/JsEventLoop.kt b/korge/src@js/korlibs/datastructure/event/JsEventLoop.kt index 2684bd402e..6c8734d731 100644 --- a/korge/src@js/korlibs/datastructure/event/JsEventLoop.kt +++ b/korge/src@js/korlibs/datastructure/event/JsEventLoop.kt @@ -11,7 +11,7 @@ open class LocalJsEventLoop( precise: Boolean = false, immediateRun: Boolean = false, ) : SyncEventLoop(precise, immediateRun) { - private var closeable: Closeable? = null + private var closeable: AutoCloseable? = null override fun start() { if (closeable != null) return @@ -39,21 +39,21 @@ object JsEventLoop : BaseEventLoop() { jsGlobalThis.setTimeout({ task() }, 0) } - override fun setTimeout(time: TimeSpan, task: () -> Unit): Closeable { + override fun setTimeout(time: TimeSpan, task: () -> Unit): AutoCloseable { val id = jsGlobalThis.setTimeout({ task() }, time.millisecondsInt) - return Closeable { jsGlobalThis.clearTimeout(id) } + return AutoCloseable { jsGlobalThis.clearTimeout(id) } } - override fun setInterval(time: TimeSpan, task: () -> Unit): Closeable { + override fun setInterval(time: TimeSpan, task: () -> Unit): AutoCloseable { val id = jsGlobalThis.setInterval({ task() }, time.millisecondsInt) - return Closeable { jsGlobalThis.clearInterval(id) } + return AutoCloseable { jsGlobalThis.clearInterval(id) } } - override fun setIntervalFrame(task: () -> Unit): Closeable { + override fun setIntervalFrame(task: () -> Unit): AutoCloseable { val globalThisDyn = jsGlobalThis.asDynamic() if (!globalThisDyn.requestAnimationFrame) return super.setIntervalFrame(task) var running = true - var gen: (() -> Unit) ? = null + var gen: (() -> Unit)? = null gen = { if (running) { task() @@ -61,6 +61,6 @@ object JsEventLoop : BaseEventLoop() { } } gen() - return Closeable { running = false } + return AutoCloseable { running = false } } } diff --git a/korge/src@js/korlibs/korge/service/storage/NativeStorage.kt b/korge/src@js/korlibs/korge/service/storage/NativeStorage.kt index f51b0e7172..b499a7d5a6 100644 --- a/korge/src@js/korlibs/korge/service/storage/NativeStorage.kt +++ b/korge/src@js/korlibs/korge/service/storage/NativeStorage.kt @@ -1,10 +1,8 @@ package korlibs.korge.service.storage -import korlibs.io.* +import korlibs.graphics.gl.* import korlibs.io.lang.* -import korlibs.io.runtime.node.* import korlibs.korge.view.* -import korlibs.memory.* import korlibs.platform.* import kotlinx.browser.* diff --git a/korge/src@jvm/korlibs/korge/awt/UIAwt.kt b/korge/src@jvm/korlibs/korge/awt/UIAwt.kt index 2f2eefe47d..dc905a31a8 100644 --- a/korge/src@jvm/korlibs/korge/awt/UIAwt.kt +++ b/korge/src@jvm/korlibs/korge/awt/UIAwt.kt @@ -11,7 +11,6 @@ import korlibs.io.file.* import korlibs.io.file.std.* import korlibs.io.lang.* import korlibs.math.geom.* -import korlibs.math.geom.Point import java.awt.* import java.awt.Rectangle import java.awt.event.* diff --git a/korge/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt b/korge/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt index 8f91236240..014dbf8e1b 100644 --- a/korge/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt +++ b/korge/src@wasmJs/korlibs/datastructure/event/WasmJsEventLoop.kt @@ -11,7 +11,7 @@ open class LocalJsEventLoop( precise: Boolean = false, immediateRun: Boolean = false, ) : SyncEventLoop(precise, immediateRun) { - private var closeable: Closeable? = null + private var closeable: AutoCloseable? = null override fun start() { if (closeable != null) return @@ -39,19 +39,19 @@ object JsEventLoop : BaseEventLoop() { window.setTimeout({ task(); null }, 0) } - override fun setTimeout(time: TimeSpan, task: () -> Unit): Closeable { + override fun setTimeout(time: TimeSpan, task: () -> Unit): AutoCloseable { val id = window.setTimeout({ task(); null }, time.millisecondsInt) - return Closeable { window.clearTimeout(id) } + return AutoCloseable { window.clearTimeout(id) } } - override fun setInterval(time: TimeSpan, task: () -> Unit): Closeable { + override fun setInterval(time: TimeSpan, task: () -> Unit): AutoCloseable { val id = window.setInterval({ task(); null }, time.millisecondsInt) - return Closeable { window.clearInterval(id) } + return AutoCloseable { window.clearInterval(id) } } - override fun setIntervalFrame(task: () -> Unit): Closeable { + override fun setIntervalFrame(task: () -> Unit): AutoCloseable { var running = true - var gen: (() -> Unit) ? = null + var gen: (() -> Unit)? = null gen = { if (running) { task() @@ -59,6 +59,6 @@ object JsEventLoop : BaseEventLoop() { } } gen() - return Closeable { running = false } + return AutoCloseable { running = false } } } diff --git a/korge/src@wasmJs/korlibs/graphics/gl/GlExt.kt b/korge/src@wasmJs/korlibs/graphics/gl/GlExt.kt index afd9dfff1d..852aa4d2c2 100644 --- a/korge/src@wasmJs/korlibs/graphics/gl/GlExt.kt +++ b/korge/src@wasmJs/korlibs/graphics/gl/GlExt.kt @@ -3,6 +3,7 @@ package korlibs.graphics.gl import korlibs.kgl.* import korlibs.graphics.* import korlibs.io.wasm.* +import korlibs.wasm.* import org.w3c.dom.* import kotlinx.browser.* @@ -14,7 +15,7 @@ object AGFactoryWebgl : AGFactory { } } -fun jsEmptyObject() = jsEmptyObj() +fun jsEmptyObject(): JsAny = jsEmptyObj() fun jsObject(vararg pairs: Pair): JsAny { val out = jsEmptyObject() diff --git a/korge/src@wasmJs/korlibs/kgl/KmlGlWasmCanvas.kt b/korge/src@wasmJs/korlibs/kgl/KmlGlWasmCanvas.kt index 0c93c42d67..0278159801 100644 --- a/korge/src@wasmJs/korlibs/kgl/KmlGlWasmCanvas.kt +++ b/korge/src@wasmJs/korlibs/kgl/KmlGlWasmCanvas.kt @@ -11,6 +11,7 @@ import korlibs.image.format.* import korlibs.io.wasm.* import korlibs.memory.* import korlibs.memory.Buffer +import korlibs.wasm.* import kotlinx.browser.* import org.khronos.webgl.* import org.w3c.dom.* diff --git a/korge/src@wasmJs/korlibs/korge/service/vibration/NativeVibration.kt b/korge/src@wasmJs/korlibs/korge/service/vibration/NativeVibration.kt index 24ad538625..a5ed852781 100644 --- a/korge/src@wasmJs/korlibs/korge/service/vibration/NativeVibration.kt +++ b/korge/src@wasmJs/korlibs/korge/service/vibration/NativeVibration.kt @@ -1,8 +1,8 @@ package korlibs.korge.service.vibration -import korlibs.io.wasm.* import korlibs.korge.view.* import korlibs.time.* +import korlibs.wasm.* import kotlinx.browser.* actual class NativeVibration actual constructor(val views: Views) { diff --git a/korge/src@wasmJs/korlibs/render/DefaultGameWindowWasm.kt b/korge/src@wasmJs/korlibs/render/DefaultGameWindowWasm.kt index 9e4d52c473..f1dfc5a2e7 100644 --- a/korge/src@wasmJs/korlibs/render/DefaultGameWindowWasm.kt +++ b/korge/src@wasmJs/korlibs/render/DefaultGameWindowWasm.kt @@ -8,10 +8,13 @@ import korlibs.image.bitmap.* import korlibs.image.format.* import korlibs.io.async.* import korlibs.io.file.* +import korlibs.io.lang.* import korlibs.io.util.* import korlibs.io.wasm.* import korlibs.kgl.* import korlibs.math.geom.* +import korlibs.render.internal.* +import korlibs.wasm.* import kotlinx.browser.* import kotlinx.coroutines.* import org.w3c.dom.* diff --git a/korge/src@wasmJs/korlibs/render/internal/KorgwWasmInternal.kt b/korge/src@wasmJs/korlibs/render/internal/KorgwWasmInternal.kt new file mode 100644 index 0000000000..069cb59ae5 --- /dev/null +++ b/korge/src@wasmJs/korlibs/render/internal/KorgwWasmInternal.kt @@ -0,0 +1,33 @@ +package korlibs.render.internal + +import korlibs.graphics.gl.* +import korlibs.graphics.gl.jsObject +import korlibs.wasm.* + + +@JsName("Error") +internal external class JsError : JsAny { + val message: String? +} + +internal external interface JsResult : JsAny { + val result: T? + val error: JsError? +} + +@JsFun("(block) => { try { return { result: block(), error: null }; } catch (e) { return { result: null, error: e }; } }") +internal external fun runCatchingJsExceptions(block: () -> T): JsResult + +internal fun wrapWasmJsExceptions(block: () -> T): T { + val result = runCatchingJsExceptions { block() } + if (result.error != null) throw Exception(result.error!!.message) + return result.result!! +} + +internal fun wrapWasmJsExceptionsUnit(block: () -> Unit) { + val result = runCatchingJsExceptions { + block() + jsEmptyObj() + } + if (result.error != null) throw Exception(result.error!!.message) +} diff --git a/korge/test/korlibs/datastructure/event/SyncEventLoopTest.kt b/korge/test/korlibs/datastructure/event/SyncEventLoopTest.kt index bc251eb3d3..f3bdc5b216 100644 --- a/korge/test/korlibs/datastructure/event/SyncEventLoopTest.kt +++ b/korge/test/korlibs/datastructure/event/SyncEventLoopTest.kt @@ -1,15 +1,21 @@ package korlibs.datastructure.event +import korlibs.concurrent.thread.* import korlibs.datastructure.closeable.* import korlibs.datastructure.thread.* +import korlibs.datastructure.thread.NativeThread +import korlibs.datastructure.thread.nativeThread +import korlibs.io.async.* import korlibs.time.* import kotlin.test.* import kotlin.time.* import kotlin.time.Duration.Companion.seconds class SyncEventLoopTest { + // @TODO: Lock.notify is not implemented on JS @Test - fun test() { + fun test() = suspendTest({ NativeThread.isSupported }) { + //fun test() = suspendTest { repeat(2) { val ep = SyncEventLoop(precise = true) val start = TimeSource.Monotonic.markNow() @@ -17,7 +23,7 @@ class SyncEventLoopTest { println("${start.elapsedNow().milliseconds}: $msg") } var times = 0 - var interval: Closeable? = null + var interval: AutoCloseable? = null ep.setTimeout(0.5.seconds) { log("timeout after 0.5 seconds") } interval = ep.setInterval(0.2.seconds) { log("interval after 0.2 seconds") diff --git a/korge/test/korlibs/korge/input/KeysEventsTest.kt b/korge/test/korlibs/korge/input/KeysEventsTest.kt index fa72e7cf78..1e978c8d95 100644 --- a/korge/test/korlibs/korge/input/KeysEventsTest.kt +++ b/korge/test/korlibs/korge/input/KeysEventsTest.kt @@ -8,6 +8,7 @@ import korlibs.korge.view.* import korlibs.math.geom.* import korlibs.time.* import kotlin.test.* +import kotlin.time.* class KeysEventsTest : ViewsForTesting() { @Test @@ -77,7 +78,7 @@ class KeysEventsTest : ViewsForTesting() { fun keyUp(key: Key) { views.dispatch(KeyEvent(KeyEvent.Type.UP, key = key)) } - fun delay(time: TimeSpan) { + fun delay(time: Duration) { views.dispatch(UpdateEvent(time)) } diff --git a/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt b/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt index 03f0a146d9..5f3bbb7cf1 100644 --- a/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt +++ b/korge/test@jvm/korlibs/datastructure/event/JvmSyncEventLoopTest.kt @@ -1,7 +1,11 @@ package korlibs.datastructure.event +import korlibs.concurrent.thread.* +import korlibs.concurrent.thread.NativeThread.Companion.sleep import korlibs.datastructure.lock.* import korlibs.datastructure.thread.* +import korlibs.datastructure.thread.NativeThread +import korlibs.datastructure.thread.nativeThread import korlibs.time.* import java.awt.* import javax.swing.* diff --git a/korge/test@jvm/korlibs/render/awt/AwtGameCanvasTest.kt b/korge/test@jvm/korlibs/render/awt/AwtGameCanvasTest.kt index 57e1f61d4b..3244dff0d5 100644 --- a/korge/test@jvm/korlibs/render/awt/AwtGameCanvasTest.kt +++ b/korge/test@jvm/korlibs/render/awt/AwtGameCanvasTest.kt @@ -1,8 +1,10 @@ package korlibs.render.awt +import korlibs.concurrent.thread.* import korlibs.datastructure.event.* import korlibs.datastructure.lock.* -import korlibs.datastructure.thread.* +import korlibs.datastructure.thread.NativeThread +import korlibs.datastructure.thread.nativeThread import korlibs.image.bitmap.* import korlibs.image.color.* import korlibs.kgl.* diff --git a/korge/test@jvm/korlibs/render/awt/AwtGameWindowTest.kt b/korge/test@jvm/korlibs/render/awt/AwtGameWindowTest.kt index 1a8adb49ec..ee729b4729 100644 --- a/korge/test@jvm/korlibs/render/awt/AwtGameWindowTest.kt +++ b/korge/test@jvm/korlibs/render/awt/AwtGameWindowTest.kt @@ -1,6 +1,9 @@ package korlibs.render.awt +import korlibs.concurrent.thread.* +import korlibs.concurrent.thread.NativeThread.Companion.sleep import korlibs.datastructure.thread.* +import korlibs.datastructure.thread.NativeThread import korlibs.image.color.* import korlibs.image.format.* import korlibs.io.async.*