From dfe68cda6d3efa3b4525bc8b4f7b25c9880b7964 Mon Sep 17 00:00:00 2001 From: MJ Date: Fri, 30 Aug 2019 14:27:38 +0200 Subject: [PATCH 01/45] Prepared for the next release. #212 --- CHANGELOG.md | 2 ++ version.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c98be56f..ea3bd9fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +#### 1.9.10-SNAPSHOT + #### 1.9.10-b2 - **[UPDATE]** Updated to Kotlin 1.3.50. diff --git a/version.txt b/version.txt index cc55e1c9..e7b756bc 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.9.10-b2 +1.9.10-SNAPSHOT From d0eaea76f2fde09a24308f675674c322d541d4c5 Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Fri, 13 Sep 2019 18:26:17 -0400 Subject: [PATCH 02/45] Added Batch.begin extension function. #214 --- .../src/main/kotlin/ktx/graphics/graphics.kt | 15 +++++++++++++ .../test/kotlin/ktx/graphics/graphicsTest.kt | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/graphics/src/main/kotlin/ktx/graphics/graphics.kt b/graphics/src/main/kotlin/ktx/graphics/graphics.kt index 54a8ae16..334c1d4f 100644 --- a/graphics/src/main/kotlin/ktx/graphics/graphics.kt +++ b/graphics/src/main/kotlin/ktx/graphics/graphics.kt @@ -48,6 +48,21 @@ inline fun B.use(projectionMatrix: Matrix4? = null, action: (B) -> U */ inline fun B.use(camera: Camera, action: (B) -> Unit) = use(camera.combined, action) +/** + * Automatically calls [Batch.begin] with the provided matrix + * @param projectionMatrix A projection matrix to set on the batch before [Batch.begin]. + */ +fun B.begin(projectionMatrix: Matrix4) { + this.projectionMatrix = projectionMatrix + begin() +} + +/** + * Sets the batch's projection matrix to the camera's [Camera.combined] matrix and calls [Batch.begin]. + * @param camera The camera's [Camera.combined] matrix will be set to the batch's projection matrix before [Batch.begin]. + */ +fun B.begin(camera: Camera) = begin(camera.combined) + /** * Automatically calls [ShaderProgram.begin] and [ShaderProgram.end]. * @param action inlined. Executed after [ShaderProgram.begin] and before [ShaderProgram.end]. diff --git a/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt b/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt index 88a131c6..98d5c14c 100644 --- a/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt +++ b/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt @@ -106,6 +106,28 @@ class GraphicsTest { verify(batch).end() } + @Test + fun `should begin with provided projection matrix`() { + val batch = mock() + val matrix = Matrix4(FloatArray(16) {it.toFloat()}) + + batch.begin(projectionMatrix = matrix) + verify(batch).projectionMatrix = matrix + verify(batch).begin() + batch.end() + } + + @Test + fun `should begin with provided camera combined matrix`() { + val batch = mock() + val camera = OrthographicCamera() + + batch.begin(camera = camera) + verify(batch).projectionMatrix = camera.combined + verify(batch).begin() + batch.end() + } + @Test fun `should begin and end ShaderProgram`() { val shaderProgram = mock() From d22d3ee7fe57890e0642c1cfa79886ff869dab86 Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Sat, 14 Sep 2019 10:17:50 -0400 Subject: [PATCH 03/45] Minor code cleanup. --- graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt b/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt index 98d5c14c..21fd11ea 100644 --- a/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt +++ b/graphics/src/test/kotlin/ktx/graphics/graphicsTest.kt @@ -112,9 +112,9 @@ class GraphicsTest { val matrix = Matrix4(FloatArray(16) {it.toFloat()}) batch.begin(projectionMatrix = matrix) + verify(batch).projectionMatrix = matrix verify(batch).begin() - batch.end() } @Test @@ -123,9 +123,9 @@ class GraphicsTest { val camera = OrthographicCamera() batch.begin(camera = camera) + verify(batch).projectionMatrix = camera.combined verify(batch).begin() - batch.end() } @Test From 13b208a2f5bcb5990754c599866b3b137390f97f Mon Sep 17 00:00:00 2001 From: MJ Date: Sun, 15 Sep 2019 13:03:06 +0200 Subject: [PATCH 04/45] Updated contributors. #214 --- .github/CONTRIBUTORS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CONTRIBUTORS.md b/.github/CONTRIBUTORS.md index 4c2fae89..0b1bb33b 100644 --- a/.github/CONTRIBUTORS.md +++ b/.github/CONTRIBUTORS.md @@ -51,3 +51,5 @@ Project contributors listed chronologically. * Contributed documentation fix. * [@cypherdare](https://github.com/cypherdare) * Improved [actors](../actors) and [graphics](../graphics) utilities. +* [@jakewilson](https://github.com/jakewilson) + * Added utilities to [graphics module](../graphics). From 7c4f14757265da318d9d8c14d09435d62955c66f Mon Sep 17 00:00:00 2001 From: maltaisn Date: Tue, 1 Oct 2019 21:18:33 -0400 Subject: [PATCH 05/45] Added more reified skin extension methods with default style name parameter --- style/src/main/kotlin/ktx/style/style.kt | 50 ++++++++- style/src/test/kotlin/ktx/style/styleTest.kt | 103 +++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/style/src/main/kotlin/ktx/style/style.kt b/style/src/main/kotlin/ktx/style/style.kt index 4147d674..be898904 100644 --- a/style/src/main/kotlin/ktx/style/style.kt +++ b/style/src/main/kotlin/ktx/style/style.kt @@ -21,6 +21,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Touchpad.TouchpadStyle import com.badlogic.gdx.scenes.scene2d.ui.Tree.TreeStyle import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle import com.badlogic.gdx.utils.GdxRuntimeException +import com.badlogic.gdx.utils.ObjectMap import kotlin.annotation.AnnotationTarget.* /** Should annotate builder methods of Scene2D [Skin]. */ @@ -71,6 +72,14 @@ inline operator fun Skin.get(name: String = defaultStyl */ inline operator fun > Skin.get(name: E): Resource = this[name.toString()] +/** + * Utility function that makes it easier to access [Skin] assets or return null if they don't exist. + * @param name name of the requested resource. Defaults to [defaultStyle]. + * @return resource of the specified type with the selected name, or `null` if it doesn't exist. + */ +inline fun Skin.optional(name: String = defaultStyle): Resource? = + this.optional(name, Resource::class.java) + /** * Utility function that makes it easier to add [Skin] assets. * @param name name of the passed resource. @@ -84,8 +93,45 @@ inline operator fun Skin.set(name: String, resource: Re * @param name name of the passed resource. * @param resource will be added to the skin and mapped to the selected name. */ -inline operator fun > Skin.set(name: E, resource: Resource) = - this.set(name.toString(), resource) +inline operator fun > Skin.set(name: E, resource: Resource) = + this.set(name.toString(), resource) + +/** + * Utility function that makes it easier to add [Skin] assets. + * @param name name of the passed resource. Defaults to [defaultStyle]. + * @param resource will be added to the skin and mapped to the selected name. + */ +inline fun Skin.add(resource: Resource, name: String = defaultStyle) = + this.add(name, resource, Resource::class.java) + +/** + * Utility function that makes it easier to add [Skin] assets under the [defaultStyle] name. + * @param resource will be added to the skin and mapped to the selected name. + */ +inline operator fun Skin.plusAssign(resource: Resource) = + this.add(resource) + +/** + * Utility function that makes it easier to remove [Skin] assets. + * @param name name of the passed resource. Defaults to [defaultStyle]. + * @throws NullPointerException if unable to find the resource. + */ +inline fun Skin.remove(name: String = defaultStyle) = + this.remove(name, Resource::class.java) + +/** + * Utility function that makes it easier to check if [Skin] contains assets. + * @param name name of the resource to look for. Defaults to [defaultStyle]. + */ +inline fun Skin.has(name: String = defaultStyle): Boolean = + this.has(name, Resource::class.java) + +/** + * Utility function that makes it easier to access all [Skin] assets of a certain type. + * @return map of the resources for the [Resource] type, or `null` if no resources of that type is in the skin. + */ +inline fun Skin.getAll(): ObjectMap? = + this.getAll(Resource::class.java) /** * Utility function for adding existing styles to the skin. Mostly for internal use. diff --git a/style/src/test/kotlin/ktx/style/styleTest.kt b/style/src/test/kotlin/ktx/style/styleTest.kt index ba30f1e8..216eb0a3 100644 --- a/style/src/test/kotlin/ktx/style/styleTest.kt +++ b/style/src/test/kotlin/ktx/style/styleTest.kt @@ -100,6 +100,33 @@ class StyleTest { reified shouldBe "Test." } + @Test + fun `should extract optional resource with default style name`() { + val skin = Skin() + + skin.add(defaultStyle, "Test.") + + skin.optional() shouldBe "Test." + } + + @Test + fun `should extract optional resource`() { + val skin = Skin() + + skin.add("mock", "Test.") + + skin.optional("mock") shouldBe "Test." + } + + @Test + fun `should extract optional resource and return null`() { + val skin = Skin() + + skin.add("asset", "Test.") + + skin.optional("mock") shouldBe null + } + @Test fun `should extract resource with default style name`() { val skin = Skin() @@ -172,6 +199,82 @@ class StyleTest { style.fontColor shouldBe Color.BLACK } + @Test + fun `should add resource with default style name`() { + val skin = Skin() + + skin.add("Test.") + + skin.get() shouldBe "Test." + } + + @Test + fun `should add resource with default style name with plusAssign`() { + val skin = Skin() + + skin += "Test." + + skin.get() shouldBe "Test." + } + + @Test + fun `should remove resource with default style name`() { + val skin = Skin() + + skin.add(defaultStyle, "Test.") + + skin.remove() + + skin.has(defaultStyle, String::class.java) shouldBe false + } + + @Test + fun `should remove resource`() { + val skin = Skin() + + skin.add("mock", "Test.") + + skin.remove("mock") + + skin.has("mock", String::class.java) shouldBe false + } + + @Test + fun `should check if resource is present`() { + val skin = Skin() + + skin.add("mock", "Test.") + + skin.has("mock") shouldBe true + skin.has("mock-2") shouldBe false + } + + @Test + fun `should check if resource with default style name is present`() { + val skin = Skin() + + skin.add(defaultStyle, "Test.") + + skin.has() shouldBe true + } + + @Test + fun `should return a map of all resources of a type`() { + val skin = Skin() + + skin.add("mock-1", "Test.") + skin.add("mock-2", "Test 2.") + + skin.getAll()!!.size shouldBe 2 + } + + @Test + fun `should return null if resource is not in skin`() { + val skin = Skin() + + skin.getAll() shouldBe null + } + @Test fun `should add color`() { val skin = Skin() From 220a835ee842f9685787837d64bd5e50319fbb40 Mon Sep 17 00:00:00 2001 From: maltaisn Date: Wed, 2 Oct 2019 18:02:33 -0400 Subject: [PATCH 06/45] Updated changelog and usage --- CHANGELOG.md | 4 ++++ style/README.md | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea3bd9fb..66bf5054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ #### 1.9.10-SNAPSHOT +- **[FEATURE]** (`ktx-style`) Added more extension methods to Skin, with reified type and name parameter that +defaults to the "default" style name. `optional`, `add`, `remove`, `has` and `getAll` extension methods were added. +The overloaded `+=` operator can also be used to add resource with "default" name. + #### 1.9.10-b2 - **[UPDATE]** Updated to Kotlin 1.3.50. diff --git a/style/README.md b/style/README.md index ee596534..55661076 100644 --- a/style/README.md +++ b/style/README.md @@ -35,6 +35,20 @@ Note that both of these functions use reified generics, so they need to be able context. For example, `val font = skin["name"]` would not compile, as the compiler would not be able to guess that instance of `BitmapFont` class is requested. +More methods were also added to allow type inference: +```Kotlin +val res: Resource? = skin.optional("name") +val found = skin.has("name") + +skin.add(resource, "name") +skin += otherResource // Uses the "default" name + +skin.remove("name") + +val map: ObjectMap? = skin.getAll() +``` +All name parameters can also be left blank to use the default style name, `"default"`. + An extension method for every style of every Scene2D widget was added to `Skin`. Each method name matches `lowerCamelCase` name of the actor class. For example, the method used to create `ScrollPaneStyle` instances is named `scrollPane`. Signature of every extension method is pretty much the same - they consume 3 parameters: style name (defaults to From 951ee090474fae323bfd0f994827f9abf37b9d31 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 3 Oct 2019 10:11:20 +0200 Subject: [PATCH 07/45] Updated ktx-style documentation. #216 --- CHANGELOG.md | 5 ++--- style/README.md | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66bf5054..2797f19a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,7 @@ #### 1.9.10-SNAPSHOT -- **[FEATURE]** (`ktx-style`) Added more extension methods to Skin, with reified type and name parameter that -defaults to the "default" style name. `optional`, `add`, `remove`, `has` and `getAll` extension methods were added. -The overloaded `+=` operator can also be used to add resource with "default" name. +- **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. +- **[FEATURE]** (`ktx-style`) The overloaded `+=` operator can now be used to add `"default"` resources to `Skin`. #### 1.9.10-b2 diff --git a/style/README.md b/style/README.md index 55661076..44aa9461 100644 --- a/style/README.md +++ b/style/README.md @@ -35,7 +35,8 @@ Note that both of these functions use reified generics, so they need to be able context. For example, `val font = skin["name"]` would not compile, as the compiler would not be able to guess that instance of `BitmapFont` class is requested. -More methods were also added to allow type inference: +Additional methods were also added to leverage type inference and skip `Class` parameters: + ```Kotlin val res: Resource? = skin.optional("name") val found = skin.has("name") @@ -47,7 +48,8 @@ skin.remove("name") val map: ObjectMap? = skin.getAll() ``` -All name parameters can also be left blank to use the default style name, `"default"`. + +Note that all `name` parameters can also be skipped to use the default style name, `"default"`. An extension method for every style of every Scene2D widget was added to `Skin`. Each method name matches `lowerCamelCase` name of the actor class. For example, the method used to create `ScrollPaneStyle` instances is named `scrollPane`. From 34cb061b3a33d9e976778b39bb958f98c2a468a3 Mon Sep 17 00:00:00 2001 From: MJ Date: Thu, 3 Oct 2019 10:17:06 +0200 Subject: [PATCH 08/45] Updated ktx-graphics documentation. #214 --- CHANGELOG.md | 1 + graphics/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2797f19a..8d02529c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ #### 1.9.10-SNAPSHOT +- **[FEATURE]** (`ktx-graphics`) Added `Batch.begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4`. - **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. - **[FEATURE]** (`ktx-style`) The overloaded `+=` operator can now be used to add `"default"` resources to `Skin`. diff --git a/graphics/README.md b/graphics/README.md index 3ba6fb54..cecfd49e 100644 --- a/graphics/README.md +++ b/graphics/README.md @@ -18,6 +18,7 @@ overriding with optional, named parameters. - `use` inlined extension methods added to `Batch`, `ShaderProgram` and `GLFrameBuffer`. They allow safe omission of the `begin()` and `end()` calls when using batches, shader programs and buffers. Note that a camera or projection matrix can also be passed to the `Batch.use` extension function to have it automatically applied to the batch's projection matrix. +- `begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4` were added to `Batch`. #### `ShapeRenderer` From e15bc6da6736b78cd5dacee99ed78a833481adec Mon Sep 17 00:00:00 2001 From: Roman Hutsaliuk Date: Mon, 7 Oct 2019 00:40:53 +0300 Subject: [PATCH 09/45] Added adapters and factory methods for JSON serializers. #217 --- .../main/kotlin/ktx/json/jsonSerializer.kt | 40 ++++++++++ .../kotlin/ktx/json/jsonSerializerTest.kt | 77 +++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 json/src/main/kotlin/ktx/json/jsonSerializer.kt create mode 100644 json/src/test/kotlin/ktx/json/jsonSerializerTest.kt diff --git a/json/src/main/kotlin/ktx/json/jsonSerializer.kt b/json/src/main/kotlin/ktx/json/jsonSerializer.kt new file mode 100644 index 00000000..ceee420d --- /dev/null +++ b/json/src/main/kotlin/ktx/json/jsonSerializer.kt @@ -0,0 +1,40 @@ +package ktx.json + +import com.badlogic.gdx.utils.Json +import com.badlogic.gdx.utils.JsonValue + +/** + * Wrapping interface around [com.badlogic.gdx.utils.Json.Serializer]. + * Provides null-safety for the methods. + */ +interface JsonSerializer : Json.Serializer { + override fun write(json: Json, obj: T, knownType: Class<*>?) + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T +} + +/** + * Wrapping interface around [com.badlogic.gdx.utils.Json.Serializer]. Provides null-safety + * and convenient interface for serializer that is only able to [read]. + * Unlike LibGDX [ReadOnlySerializer][com.badlogic.gdx.utils.Json.ReadOnlySerializer], the [write] + * method throws [UnsupportedOperationException] + */ +interface ReadOnlyJsonSerializer : Json.Serializer { + override fun write(json: Json, obj: T, knownType: Class<*>?) = throw UnsupportedOperationException("Read-only serializer does not support writing") + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T +} + +/** + * Factory function to create a [ReadOnlyJsonSerializer] from lambda + */ +inline fun readOnlySerializer(crossinline reader: (Json, JsonValue, Class<*>?) -> T): Json.Serializer = + object : ReadOnlyJsonSerializer { + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T = reader(json, jsonData, type) + } + +/** + * Factory function to create a simplified [ReadOnlyJsonSerializer], which accepts only [JsonValue]. + */ +inline fun readOnlySerializer(crossinline read: (JsonValue) -> T): Json.Serializer = + object : ReadOnlyJsonSerializer { + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T = read(jsonData) + } diff --git a/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt b/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt new file mode 100644 index 00000000..5ebff9fc --- /dev/null +++ b/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt @@ -0,0 +1,77 @@ +package ktx.json + +import com.badlogic.gdx.utils.Json +import com.badlogic.gdx.utils.JsonValue +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import io.kotlintest.matchers.shouldEqual +import io.kotlintest.matchers.shouldThrow +import org.junit.Test + +@Suppress("unused") +class `should implement ReadOnlyJsonSerializer with no 'write' method overriden` : ReadOnlyJsonSerializer { + // Guarantees that [write] method is optional to implement. + + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T { + throw NotImplementedError() + } +} + +class ReadOnlyJsonSerializerTest { + + @Test + fun `default implementation for 'write' method should throw UnsupportedOperationException`() { + shouldThrow { + object : ReadOnlyJsonSerializer { + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): Any = throw NotImplementedError() + }.write(Json(), JsonValue(JsonValue.ValueType.`object`), Any::class.java) + } + } +} + +class ReadOnlyJsonSerializerFactoryTest { + @Test + fun `should return readonly serializer that calls lambda function with 3 parameters`() { + val json = Json() + val jsonValue = JsonValue(JsonValue.ValueType.`object`) + val readFunction: (Json, JsonValue, Class<*>?) -> Data = mock() + + readOnlySerializer(readFunction).read(json, jsonValue, Data::class.java) + + verify(readFunction).invoke(json, jsonValue, Data::class.java) + } + + @Test + fun `should return readonly serializer that returns result of calling lambda with 3 parameters`() { + val data = Data(intField = 10, stringField = "Test", doubleField = 11.254) + val jsonData = JsonValue(JsonValue.ValueType.`object`) + val readFunction: (Json, JsonValue, Class<*>?) -> Data = { _, _, _ -> data } + + val value = readOnlySerializer(readFunction).read(Json(), jsonData, Data::class.java) + + value shouldEqual data + } + + @Test + fun `should return readonly serializer that calls lambda function with 2 parameters`() { + val jsonData = JsonValue(JsonValue.ValueType.`object`) + val readFunction: (JsonValue) -> Data = mock() + + readOnlySerializer(readFunction).read(Json(), jsonData, Data::class.java) + + verify(readFunction).invoke(jsonData) + } + + @Test + fun `should return readonly serializer that returns result of calling lambda with 2 parameters`() { + val data = Data(intField = 10, stringField = "Test", doubleField = 11.254) + val jsonData = JsonValue(JsonValue.ValueType.`object`) + val readFunction: (JsonValue) -> Data = { data } + + val value = readOnlySerializer(readFunction).read(Json(), jsonData, Data::class.java) + + value shouldEqual data + } + + data class Data(val intField: Int, val stringField: String, val doubleField: Double) +} From d63707b233ebe7d74b9ae05187e55c352a5386f1 Mon Sep 17 00:00:00 2001 From: Roman Hutsaliuk Date: Mon, 7 Oct 2019 01:12:07 +0300 Subject: [PATCH 10/45] Updated changelog and readme. #217 --- CHANGELOG.md | 2 ++ json/README.md | 55 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d02529c..5c230380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ - **[FEATURE]** (`ktx-graphics`) Added `Batch.begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4`. - **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. - **[FEATURE]** (`ktx-style`) The overloaded `+=` operator can now be used to add `"default"` resources to `Skin`. +- **[FEATURE]** (`ktx-json`) Added `JsonSerializer` and `ReadOnlyJsonSerializer` adapters to facilitate writing custom serializers. +- **[FEATURE]** (`ktx-json`) Added `readOnlySerializer()` factory functions to simplify creation of `ReadOnlyJsonSerializer`. #### 1.9.10-b2 diff --git a/json/README.md b/json/README.md index 119df04a..23bcb51d 100644 --- a/json/README.md +++ b/json/README.md @@ -8,18 +8,23 @@ The LibGDX JSON reader and writer methods often consume `Class` parameters, whic `Type::class.java` syntax on Kotlin users. Fortunately, Kotlin brings reified generics which effectively allow passing a `Class` parameter through a generic type. This module mostly offers extension methods with reified generics to avoid using `::class.java` in your code, as well as to allow type inference -and better type safety. +and better type safety. Additionally it provides couple of classes to facilitate creation of custom +serializers. ### Guide -KTX brings the following extension methods to LibGDX `Json` API: - -- `fromJson` -- `addClassTag` -- `getTag` -- `setElementType` -- `setSerializer` -- `readValue` +KTX brings the following additions to LibGDX `Json` API: +- Extension methods and functions: + - `fromJson` + - `addClassTag` + - `getTag` + - `setElementType` + - `setSerializer` + - `readValue` + - `readOnlySerializer` +- Classes: + - `JsonSerializer` + - `ReadOnlyJsonSerializer` All of these extension methods are consistent with the official `Json` API, but provide inlined reified typing to avoid passing `Class` instances to improve code readability. @@ -59,7 +64,7 @@ json.setElementType("cards") json.setSerializer(object : Json.Serializer() { /* ... */ }) ``` -A class with a custom serializer: +A class with custom serializable implementation: ```kotlin import com.badlogic.gdx.math.Vector2 @@ -92,7 +97,37 @@ val player: Player = json.fromJson("""{ "pos": {"x": 10, "y": 10}, "cards": [1, 2, 3, 5, 8, 13] }""") +``` + +A custom serializer class: +```kotlin +import ktx.json.* +import com.badlogic.gdx.math.Vector2 + +class Vector2AsArraySerializer: JsonSerializer { + override fun write(json: Json, obj: Vector2, knownType: Class<*>?) { + json.writeArrayStart() + json.writeValue(obj.x) + json.writeValue(obj.y) + json.writeArrayEnd() + } + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): Vector2 + = jsonData.asFloatArray().let { (x, y) -> Vector2(x, y) } +} + +// You can also create a read-only serializer: +val vector2AsArraySerializer = readOnlySerializer { jsonValue -> + jsonValue.asFloatArray().let { (x, y) -> Vector2(x, y) } +} + +val json = Json() +json.setSerializer(Vector2AsArraySerializer()) + +val player: Player = json.fromJson("""{ + "pos": [10, 10] + "cards": [1, 2, 3, 5, 8, 13] +}""") ``` ### Alternatives From fff7c9572c8adec996fd4cb6d51fc44fb66d48c8 Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 7 Oct 2019 18:07:15 +0200 Subject: [PATCH 11/45] JSON serializers refactoring. #217 --- json/README.md | 30 +++++++++++-------- .../{jsonSerializer.kt => serializers.kt} | 18 +++++------ .../kotlin/ktx/json/jsonSerializerTest.kt | 22 +++++++------- 3 files changed, 37 insertions(+), 33 deletions(-) rename json/src/main/kotlin/ktx/json/{jsonSerializer.kt => serializers.kt} (60%) diff --git a/json/README.md b/json/README.md index 23bcb51d..471b1878 100644 --- a/json/README.md +++ b/json/README.md @@ -26,8 +26,8 @@ KTX brings the following additions to LibGDX `Json` API: - `JsonSerializer` - `ReadOnlyJsonSerializer` -All of these extension methods are consistent with the official `Json` API, but provide inlined reified typing -to avoid passing `Class` instances to improve code readability. +All of these extension methods are consistent with the official `Json` API, but provide improved Kotlin typing +signatures and inlined reified type parameters to avoid passing `Class` instances and improve code readability. A comparison of the APIs when used from Kotlin: @@ -49,6 +49,7 @@ json.fromJson(file) Creating a new `Json` serializer instance with custom parameters: ```kotlin +import com.badlogic.gdx.utils.Json import ktx.json.* val json = Json() @@ -90,6 +91,7 @@ class Player( Parsing a JSON object: ```kotlin +import com.badlogic.gdx.utils.Json import ktx.json.* val json = Json() @@ -99,26 +101,28 @@ val player: Player = json.fromJson("""{ }""") ``` -A custom serializer class: +Using a custom serializer: ```kotlin -import ktx.json.* import com.badlogic.gdx.math.Vector2 +import com.badlogic.gdx.utils.Json +import com.badlogic.gdx.utils.JsonValue +import ktx.json.* class Vector2AsArraySerializer: JsonSerializer { - override fun write(json: Json, obj: Vector2, knownType: Class<*>?) { + override fun read(json: Json, jsonValue: JsonValue, type: Class<*>?): Vector2 + = jsonValue.asFloatArray().let { (x, y) -> Vector2(x, y) } + + override fun write(json: Json, value: Vector2, type: Class<*>?) { json.writeArrayStart() - json.writeValue(obj.x) - json.writeValue(obj.y) + json.writeValue(value.x) + json.writeValue(value.y) json.writeArrayEnd() } - - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): Vector2 - = jsonData.asFloatArray().let { (x, y) -> Vector2(x, y) } } -// You can also create a read-only serializer: -val vector2AsArraySerializer = readOnlySerializer { jsonValue -> - jsonValue.asFloatArray().let { (x, y) -> Vector2(x, y) } +// A read-only serializer can be created with a simple lambda expression: +val vector2AsArraySerializer = readOnlySerializer { + it.asFloatArray().let { (x, y) -> Vector2(x, y) } } val json = Json() diff --git a/json/src/main/kotlin/ktx/json/jsonSerializer.kt b/json/src/main/kotlin/ktx/json/serializers.kt similarity index 60% rename from json/src/main/kotlin/ktx/json/jsonSerializer.kt rename to json/src/main/kotlin/ktx/json/serializers.kt index ceee420d..159701cb 100644 --- a/json/src/main/kotlin/ktx/json/jsonSerializer.kt +++ b/json/src/main/kotlin/ktx/json/serializers.kt @@ -5,11 +5,11 @@ import com.badlogic.gdx.utils.JsonValue /** * Wrapping interface around [com.badlogic.gdx.utils.Json.Serializer]. - * Provides null-safety for the methods. + * Improves typing by adding nullability information and changes default parameter names. */ interface JsonSerializer : Json.Serializer { - override fun write(json: Json, obj: T, knownType: Class<*>?) - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T + override fun read(json: Json, jsonValue: JsonValue, type: Class<*>?): T + override fun write(json: Json, value: T, type: Class<*>?) } /** @@ -18,17 +18,17 @@ interface JsonSerializer : Json.Serializer { * Unlike LibGDX [ReadOnlySerializer][com.badlogic.gdx.utils.Json.ReadOnlySerializer], the [write] * method throws [UnsupportedOperationException] */ -interface ReadOnlyJsonSerializer : Json.Serializer { - override fun write(json: Json, obj: T, knownType: Class<*>?) = throw UnsupportedOperationException("Read-only serializer does not support writing") - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T +interface ReadOnlyJsonSerializer : JsonSerializer { + override fun write(json: Json, value: T, type: Class<*>?) = + throw UnsupportedOperationException("Read-only serializers do not support write method.") } /** - * Factory function to create a [ReadOnlyJsonSerializer] from lambda + * Factory function to create a [ReadOnlyJsonSerializer] from lambda. */ inline fun readOnlySerializer(crossinline reader: (Json, JsonValue, Class<*>?) -> T): Json.Serializer = object : ReadOnlyJsonSerializer { - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T = reader(json, jsonData, type) + override fun read(json: Json, jsonValue: JsonValue, type: Class<*>?): T = reader(json, jsonValue, type) } /** @@ -36,5 +36,5 @@ inline fun readOnlySerializer(crossinline reader: (Json, JsonValue, Class<*> */ inline fun readOnlySerializer(crossinline read: (JsonValue) -> T): Json.Serializer = object : ReadOnlyJsonSerializer { - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T = read(jsonData) + override fun read(json: Json, jsonValue: JsonValue, type: Class<*>?): T = read(jsonValue) } diff --git a/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt b/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt index 5ebff9fc..a66215e9 100644 --- a/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt +++ b/json/src/test/kotlin/ktx/json/jsonSerializerTest.kt @@ -8,23 +8,23 @@ import io.kotlintest.matchers.shouldEqual import io.kotlintest.matchers.shouldThrow import org.junit.Test -@Suppress("unused") -class `should implement ReadOnlyJsonSerializer with no 'write' method overriden` : ReadOnlyJsonSerializer { - // Guarantees that [write] method is optional to implement. - - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): T { - throw NotImplementedError() - } +/** + * Guarantees that [write] method is optional to implement. + */ +@Suppress("unused", "ClassName") +class `should implement ReadOnlyJsonSerializer with no 'write' method overridden` : ReadOnlyJsonSerializer { + override fun read(json: Json, jsonValue: JsonValue, type: Class<*>?): T = throw NotImplementedError() } class ReadOnlyJsonSerializerTest { - @Test fun `default implementation for 'write' method should throw UnsupportedOperationException`() { + val readOnlyJsonSerializer = object : ReadOnlyJsonSerializer { + override fun read(json: Json, jsonValue: JsonValue, type: Class<*>?): Any = throw NotImplementedError() + } + shouldThrow { - object : ReadOnlyJsonSerializer { - override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): Any = throw NotImplementedError() - }.write(Json(), JsonValue(JsonValue.ValueType.`object`), Any::class.java) + readOnlyJsonSerializer.write(Json(), JsonValue(JsonValue.ValueType.`object`), Any::class.java) } } } From 70da3d1eee05ed39eb424a7b44900994382ecdfa Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 7 Oct 2019 18:10:49 +0200 Subject: [PATCH 12/45] Updated contributors. #217 --- .github/CONTRIBUTORS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CONTRIBUTORS.md b/.github/CONTRIBUTORS.md index 0b1bb33b..72406a25 100644 --- a/.github/CONTRIBUTORS.md +++ b/.github/CONTRIBUTORS.md @@ -53,3 +53,5 @@ Project contributors listed chronologically. * Improved [actors](../actors) and [graphics](../graphics) utilities. * [@jakewilson](https://github.com/jakewilson) * Added utilities to [graphics module](../graphics). +* [@rhutsaliuk](https://github.com/rhutsaliuk) + * Contributed [json](../json) serializers utilities. From c175d13b8029ad8bce71b8f1455baa5a66944ef2 Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 7 Oct 2019 18:14:07 +0200 Subject: [PATCH 13/45] Documentation fix in ktx-json. #217 --- json/src/main/kotlin/ktx/json/serializers.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/src/main/kotlin/ktx/json/serializers.kt b/json/src/main/kotlin/ktx/json/serializers.kt index 159701cb..e743842c 100644 --- a/json/src/main/kotlin/ktx/json/serializers.kt +++ b/json/src/main/kotlin/ktx/json/serializers.kt @@ -15,8 +15,8 @@ interface JsonSerializer : Json.Serializer { /** * Wrapping interface around [com.badlogic.gdx.utils.Json.Serializer]. Provides null-safety * and convenient interface for serializer that is only able to [read]. - * Unlike LibGDX [ReadOnlySerializer][com.badlogic.gdx.utils.Json.ReadOnlySerializer], the [write] - * method throws [UnsupportedOperationException] + * Unlike LibGDX [com.badlogic.gdx.utils.Json.ReadOnlySerializer], the default implementation of + * the [write] method throws [UnsupportedOperationException]. */ interface ReadOnlyJsonSerializer : JsonSerializer { override fun write(json: Json, value: T, type: Class<*>?) = From 61e2efb3d39fc1e6e76078fde32f586c7ff70f2d Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 7 Oct 2019 18:32:39 +0200 Subject: [PATCH 14/45] Updated alternative dependency injection libraries. #6 --- inject/README.md | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/inject/README.md b/inject/README.md index 5313fbad..0bed79e4 100644 --- a/inject/README.md +++ b/inject/README.md @@ -16,9 +16,9 @@ Java dependency injection mechanisms usually rely on annotations and compile-tim its inline functions, allows to omit the reflection and annotations usage altogether, while still providing a pleasant DSL. -Why not use an existing Kotlin DI library? `ktx-inject` is a tiny extension consisting a single source file with about -150 lines, most of which are the documentation. Being as lightweight as possible and generating little to no garbage at -runtime, it aims to be a viable choice for even the slowest devices out there. It sacrifices extra features for +Why not use an existing Kotlin DI library? `ktx-inject` is a tiny extension consisting a single source file with a few +hundred lines, most of which are the documentation. Being as lightweight as possible and generating little to no garbage +at runtime, it aims to be a viable choice for even the slowest devices out there. It sacrifices extra features for simplicity and nearly zero overhead at runtime. ### Guide @@ -133,7 +133,7 @@ Removing all components from the `Context` and disposing of all `Disposable` sin context.dispose() ``` -### Implementation notes +### Notes on implementation and design choices > How does it work? @@ -145,23 +145,19 @@ dead simple and aims to introduce as little runtime overhead as possible. > No scopes? Huh? -How often do you need these in simple games, anyway? Agreed: more complex projects might benefit from features of mature -projects like [Kodein](https://github.com/SalomonBrys/Kodein), but in most simple games you just end up needing some -glue between the components. Sometimes simplicity is something you aim for. +How often do you need these in simple games, anyway? More complex projects might benefit from features of mature +projects like [Koin](https://insert-koin.io/), but in most simple games you just end up needing some glue between +the components. Sometimes simplicity is something you aim for. As for testing scope, it should be obvious that you can just register different components during testing. Don't worry, classes using `ktx-inject` are usually trivial to test. > Not even any named providers? -Nope. Providers are mapped to the class of instances that they return and that's it. Criteria systems - which are a +Nope. Providers are mapped to the class of instances that they return - and that's it. Criteria systems - which are a sensible alternative to simple string names - are somewhat easy to use when your system is based on annotations, but we don't have much to work with when the goal is simplicity. -If desperately want to use string as IDs of components, create your custom container class with overloaded `get` or -`invoke` operator - it could be worse than `inject()["player"]`, if you think about it. I've certainly _seen_ -worse! - > Kodein-style single-parameter factories, anyone? It seems that Kodein keeps all its "providers" as single-parameter functions. To avoid wrapping all no-arg providers @@ -184,19 +180,12 @@ overhead - this pretty much sums up the strong sides of `ktx-inject`. ### Alternatives -- [Kodein](https://github.com/SalomonBrys/Kodein) is a powerful, yet simple dependency injection framework written in -Kotlin. It also detects circular dependencies and is able to pretty-print pretty much anything. While it would require -additional benchmarking, this library *might be* slightly less efficient due to how it stores its data - **KTX** should -keep less meta-data at runtime and create less objects overall, limiting garbage collection. If you ever feel the need -for a more complex DI system, this is probably the way to go. -- [Injekt](https://github.com/kohesive/injekt) is another dependency injection library written in Kotlin. It seems that -the developer moved on to the *Kodein* project, although if you prefer *Injekt* API, it still seems viable to use. +- [Koin](https://insert-koin.io/), [Kodein](https://github.com/Kodein-Framework/Kodein-DI) are powerful Kotlin +dependency injection frameworks that support multiple platforms. If you ever feel that `ktx-inject` is not enough +for your use case, try these out. - [Dagger](http://google.github.io/dagger/) is a Java dependency injection library based on annotations and compile-time code generation. It generates human-readable POJO classes, which makes it both easier to debug and more efficient that the usual reflection-based solutions. However, it is harder to set up and Kotlin solutions usually offer better syntax. -To be honest, field injection with annotations works great in Java, but can be quite annoying in Kotlin with its -`lateinit`, `?` and whatnot - actually, annotation-based dependency injection syntax -[can be *less* verbose in Java than in Kotlin](https://stackoverflow.com/questions/37388357/which-is-the-preferred-syntax-when-using-annotation-based-dependency-injection-i). - [Spring](https://spring.io/) is a powerful dependency injection framework with automatic component scan. It relies on runtime class analysis with reflection, which generally makes it less efficient than Dagger or most Kotlin solutions. Thanks to its huge ecosystem and useful extensions, it might be a good solution for complex desktop games. Otherwise it From 016d526481cf5cbcacebf7a16991ee2032999405 Mon Sep 17 00:00:00 2001 From: dakeese Date: Tue, 26 Nov 2019 23:01:03 -0500 Subject: [PATCH 15/45] Add AssetGroup --- assets/README.md | 16 +++ assets/src/main/kotlin/ktx/assets/assets.kt | 99 +++++++++++++++++++ .../src/test/kotlin/ktx/assets/assetsTest.kt | 85 +++++++++++++++- 3 files changed, 199 insertions(+), 1 deletion(-) diff --git a/assets/README.md b/assets/README.md index b5a70461..c2ffa51c 100644 --- a/assets/README.md +++ b/assets/README.md @@ -58,6 +58,22 @@ loaded in the first place. Typical usage: `assetManager.unloadSafely("test.png") exception thrown during reloading. Note that `AssetManager` can throw `GdxRuntimeException` if the asset was not loaded yet. - `AssetManager.getLoader` and `setLoader` extension methods with reified types added to ease handling of `AssetLoader` instances registered in the `AssetManager`. +- The `AssetGroup` class is provided for easily grouping together assets so they can be managed as a group through calls +such as `loadAll()` or `unloadAll()`. The intended use is to subclass `AssetGroup` and +list its member assets as properties using `AssetGroup.asset()` or `AssetGroup.delayedAsset()`. It also allows for using +a common prefix for the file names of the group in case they are stored in a specific subdirectory. For example: +```Kotlin +class MapScreenAssets(manager: AssetManager) : AssetGroup(manager, "mapScreen/"){ + val atlas by asset("atlas.json") + val music by asset("mapScreen.ogg") +} + +val mapScreenAssets = MapScreenAssets(manager) +//... +manager.finishLoading() +//... +mapScreenAssets.dispose() +``` Note: if you can use coroutines in your project, [`ktx-assets-async`](../assets-async) module provides a lightweight coroutines-based alternative to `AssetManager` that can greatly simplify your asset loading code. diff --git a/assets/src/main/kotlin/ktx/assets/assets.kt b/assets/src/main/kotlin/ktx/assets/assets.kt index 7f99b7c6..c80c5ea7 100644 --- a/assets/src/main/kotlin/ktx/assets/assets.kt +++ b/assets/src/main/kotlin/ktx/assets/assets.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.assets.AssetManager import com.badlogic.gdx.assets.loaders.AssetLoader import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.utils.GdxRuntimeException +import com.badlogic.gdx.utils.ObjectSet import kotlin.reflect.KProperty /** @@ -70,6 +71,7 @@ class ManagedAsset(val manager: AssetManager, val assetDescriptor: AssetDe override fun finishLoading() { if (!isLoaded()) manager.finishLoadingAsset(assetDescriptor.fileName) } + override fun toString(): String = "ManagedAsset<${assetDescriptor.type.simpleName}>(${assetDescriptor.fileName})" } /** @@ -95,6 +97,7 @@ class DelayedAsset(val manager: AssetManager, val assetDescriptor: AssetDe manager.finishLoadingAsset(assetDescriptor.fileName) } } + override fun toString(): String = "DelayedAsset<${assetDescriptor.type.simpleName}>(${assetDescriptor.fileName})" } /** @@ -212,3 +215,99 @@ inline fun > AssetM suffix: String? = null) { setLoader(Type::class.java, suffix, assetLoader) } + +/** + * A class for managing a group of assets together, such that they can be loaded or unloaded en masse. + * [AssetGroup] should be subclassed and the assets should be defined by properties in the subclass by using [asset] or, + * [delayedAsset], which can be used as delegates. If not using delegates, it is possible to selectively load and + * unload them, in which case it is not advised to use [loadAll] and [unloadAll], because AssetManager's reference + * counts may be thrown off. + * + * If fine control over individual assets of this group is not needed, a strategy to avoid the complexity of + * AssetManager's reference counting is to: + * - Use [asset]/[delayedAsset] exclusively as delegates. + * - Do not mix and match use of [asset] and [delayedAsset]. + * - Never access any of the asset properties until the group is finished loading. + * - Do not initially call [loadAll] if using [asset] (as opposed to [delayedAsset]). + * @param manager The [AssetManager] that will handle loading and unloading of this group. + * @param filePrefix A string that will be prefixed to any file path parameter passed to [asset] or [delayedAsset]. + */ +abstract class AssetGroup(val manager: AssetManager, protected val filePrefix: String = "") { + /** The backing set containing the assets of this group. There is no need to manually add assets to this set if using + * [asset] or [delayedAsset]. */ + protected val members = ObjectSet>() + + /** Queues all assets of this group for loading by the associated [AssetManager]. + * If any assets of this group are already loaded, their load counts in the AssetManager will still be incremented. */ + fun loadAll() { + for (member in members) + member.load() + } + + /** Unloads all of the assets in this group. If any of the assets are dependencies of assets outside this group, or if + * they were loaded more than once, their load counts will only be decremented by one and the associated [AssetManager] + * will retain them. */ + fun unloadAll() { + for (member in members) + member.unload() + } + + /** Unloads all of the assets in this group, catching any exceptions. If any of the assets are dependencies of + * assets outside this group, or if they were loaded more than once, their load counts will only be decremented by one + * and the associated [AssetManager] will retain them. + * @param onError Called in response to each caught exception. By default, the exceptions are ignored. + * */ + inline fun unloadAllSafely(onError: (Asset<*>, Exception) -> Unit = { _, exception -> exception.ignore() }) { + for (member in `access$members`){ + try { + member.unload() + } catch (exception: Exception) { + onError(member, exception) + } + } + } + + /** Updates the associated [AssetManager] if the members of this group are not finished loading. This does not + * prioritize this group's assets, but it might provide early feedback that the members of this specific group are + * ready. + * @return Whether the assets of this group are finished loading. + */ + fun update() = isLoaded() || manager.update() + + /** Blocks until all members of this group are loaded. This does not prioritize this group's members, so assets from + * outside this group may be loaded as well. */ + fun finishLoading() { + while (!isLoaded()) { + if (manager.update()) break + } + } + + /** @return true if all of the assets in this group are finished being loaded by the associated [AssetManager]. */ + fun isLoaded() = members.all { it.isLoaded() } + + /** Creates a delegate for an asset, queues it for loading, and registers it as a member of this group. Beware that + * calling [loadAll] might increment the [AssetManager]'s reference count for this asset an additional time. + * @param fileName Name of the file used to reference this asset by the AssetManager. This value will be prefixed with + * [filePrefix]. + * @param params optional asset loading parameters which might affect how the assets are loaded. Can be null. + * @return an [Asset], registered as a member of this AssetGroup. + * */ + protected inline fun asset(fileName: String, params: AssetLoaderParameters? = null) = + manager.load("$filePrefix$fileName", params).also { members.add(it) } + + /** Creates a delegate for an asset and registers it as a member of this group. It is not queued for loading. Beware + * that if the asset property (or delegated property) of the returned [Asset] is accessed before it is queued for + * loading, it will eagerly be loaded by the [AssetManager]. This may cause [loadAll] to throw off AssetManager's + * reference counts. + * @param fileName Name of the file used to reference this asset by the AssetManager. This value will be prefixed with + * [filePrefix]. + * @param params optional asset loading parameters which might affect how the assets are loaded. Can be null. + * @return an [Asset], registered as a member of this AssetGroup. + * */ + protected inline fun delayedAsset(fileName: String, params: AssetLoaderParameters? = null) = + manager.loadOnDemand("$filePrefix$fileName", params).also { members.add(it) } + + @PublishedApi + internal val `access$members`: ObjectSet> + get() = members +} \ No newline at end of file diff --git a/assets/src/test/kotlin/ktx/assets/assetsTest.kt b/assets/src/test/kotlin/ktx/assets/assetsTest.kt index 67c4d9a3..e18ed5a4 100644 --- a/assets/src/test/kotlin/ktx/assets/assetsTest.kt +++ b/assets/src/test/kotlin/ktx/assets/assetsTest.kt @@ -168,6 +168,15 @@ class AssetsTest { assertFalse(assetManager.isLoaded("test")) } + @Test + fun `should ignore exception due to prior disposal`() { + assetManager.load("test") + assetManager.finishLoading() + val mockAsset = assetManager.get("test") + mockAsset.dispose() + assetManager.unloadSafely("test") + } + @Test fun `should unload asset handling exception`() { assetManager.load("test") @@ -431,6 +440,78 @@ class AssetsTest { assertSame(loader, manager.getLoader(MockAsset::class.java, ".mock")) } + + @Test + fun `should load and unload members`() { + class TestAssetGroup : AssetGroup(assetManager){ + val member1 by asset("member1") + val member2 by asset("member2") + } + + val group = TestAssetGroup() + group.finishLoading() + assert(group.isLoaded()) + + val mockAssets = listOf(group.member1, group.member2) + group.unloadAll() + assert(!group.isLoaded()) + for (mockAsset in mockAssets) + assert(mockAsset.disposed) + } + + @Test + fun `should load by update`() { + class TestAssetGroup : AssetGroup(assetManager){ + val member1 by asset("member1") + val member2 by asset("member2") + val member3 by asset("member3") + } + val nonmember = assetManager.load("nonmember") + + val group = TestAssetGroup() + nonmember.load() + while (true){ + if (group.update()) + break + } + assert(group.isLoaded()) + } + + @Test + fun `should catch exceptions`() { + class TestAssetGroup : AssetGroup(assetManager){ + val member1 by asset("member1") + val member2 by asset("member2") + } + + val group = TestAssetGroup() + with(group) { + finishLoading() + member1.dispose() + member2.dispose() + } + + var caught = 0 + group.unloadAllSafely { _, _ -> + caught++ + } + assertEquals(caught, 2) + } + + @Test + fun `should apply prefix`() { + class TestAssetGroup : AssetGroup(assetManager, "prefix/") { + val member1 = delayedAsset("member1") + val member2 = delayedAsset("member2") + } + + val group = TestAssetGroup().apply { + loadAll() + manager.finishLoading() + } + for (i in 1..2) + assert(group.manager.isLoaded("prefix/member$i")) + } } /** @@ -448,6 +529,7 @@ private fun managerWithMockAssetLoader() = AssetManager().apply { class MockAsset(val data: String, val additional: String?) : Disposable { var disposed = false override fun dispose() { + require(!disposed) { "Was already disposed!" } // Simulate behavior of some Gdx assets disposed = true } } @@ -477,4 +559,5 @@ class MockAssetLoader(fileHandleResolver: FileHandleResolver) : /** Allows to set [MockAsset.additional] via loader. Tests assets parameters API. */ class MockParameter(val additional: String?) : AssetLoaderParameters() -} + +} \ No newline at end of file From de85ba04bc4666b7bc3f1b61b18959a5ccf3c26d Mon Sep 17 00:00:00 2001 From: dakeese Date: Wed, 27 Nov 2019 17:48:21 -0500 Subject: [PATCH 16/45] Cleanup --- assets/README.md | 22 ++++++++++++++++----- assets/src/main/kotlin/ktx/assets/assets.kt | 7 ++----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/assets/README.md b/assets/README.md index c2ffa51c..21e33ac3 100644 --- a/assets/README.md +++ b/assets/README.md @@ -63,16 +63,28 @@ such as `loadAll()` or `unloadAll()`. The intended use is to subclass `AssetGrou list its member assets as properties using `AssetGroup.asset()` or `AssetGroup.delayedAsset()`. It also allows for using a common prefix for the file names of the group in case they are stored in a specific subdirectory. For example: ```Kotlin -class MapScreenAssets(manager: AssetManager) : AssetGroup(manager, "mapScreen/"){ +/** A group of assets that are stored in the mapScreen directory and are used only on the map screen. */ +class MapScreenAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "mapScreen/"){ val atlas by asset("atlas.json") val music by asset("mapScreen.ogg") } val mapScreenAssets = MapScreenAssets(manager) -//... -manager.finishLoading() -//... -mapScreenAssets.dispose() + +// Then, when ready to load them: +// Using asset() to create the assets pre-queues them once the group is instantiated. +// If you were using delayedAsset() instead, you would call mapScreenAssets.loadAll() when ready to queue them. +if (mapScreenAssets.update()) { + // The member assets are now ready to use. +} else { + // Continue showing loading screen, for example. +} + +// Alternatively, mapScreenAsset.finishLoading() would block until the assets are loaded, possibly finishing earlier +// than manager.finishLoading() would. + +// When finished with these assets: +mapScreenAssets.unloadAll() ``` Note: if you can use coroutines in your project, [`ktx-assets-async`](../assets-async) module provides a lightweight diff --git a/assets/src/main/kotlin/ktx/assets/assets.kt b/assets/src/main/kotlin/ktx/assets/assets.kt index c80c5ea7..663ca8dd 100644 --- a/assets/src/main/kotlin/ktx/assets/assets.kt +++ b/assets/src/main/kotlin/ktx/assets/assets.kt @@ -257,8 +257,8 @@ abstract class AssetGroup(val manager: AssetManager, protected val filePrefix: S * and the associated [AssetManager] will retain them. * @param onError Called in response to each caught exception. By default, the exceptions are ignored. * */ - inline fun unloadAllSafely(onError: (Asset<*>, Exception) -> Unit = { _, exception -> exception.ignore() }) { - for (member in `access$members`){ + fun unloadAllSafely(onError: (Asset<*>, Exception) -> Unit = { _, exception -> exception.ignore() }) { + for (member in members) { try { member.unload() } catch (exception: Exception) { @@ -307,7 +307,4 @@ abstract class AssetGroup(val manager: AssetManager, protected val filePrefix: S protected inline fun delayedAsset(fileName: String, params: AssetLoaderParameters? = null) = manager.loadOnDemand("$filePrefix$fileName", params).also { members.add(it) } - @PublishedApi - internal val `access$members`: ObjectSet> - get() = members } \ No newline at end of file From 1e881811540631959dbfe0e172f70ea08d28647d Mon Sep 17 00:00:00 2001 From: dakeese Date: Wed, 27 Nov 2019 21:31:46 -0500 Subject: [PATCH 17/45] Expand readme example --- assets/README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/assets/README.md b/assets/README.md index 21e33ac3..b6437c48 100644 --- a/assets/README.md +++ b/assets/README.md @@ -63,27 +63,38 @@ such as `loadAll()` or `unloadAll()`. The intended use is to subclass `AssetGrou list its member assets as properties using `AssetGroup.asset()` or `AssetGroup.delayedAsset()`. It also allows for using a common prefix for the file names of the group in case they are stored in a specific subdirectory. For example: ```Kotlin -/** A group of assets that are stored in the mapScreen directory and are used only on the map screen. */ +/** An AssetGroup intended for instantiating at the time it should start being loaded, or an AssetGroup that does not + * share its AssetManager with anything else. The assets are specified by using asset() so they are immediately + * queued with the AssetManager when the class is instantiated. */ +class UIAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "ui/"){ + val skin by asset("skin.json") + val clickSound by asset("mapScreen.wav") +} + +val uiAssets = UIAssets(manager) +// No need to queue them. They are pre-queued by instantiating the class. +uiAssets.finishLoading() // Block until they are finished loading. Incremental loading with update() is also available. + +/** An AssetGroup intended for instantiating before its members are needed (possibly referenced among other AssetGroups + * as non-nullable properties). The assets are specified using delayedAsset() so they are not queued for loading until + * loadAll() is called on the group. */ class MapScreenAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "mapScreen/"){ - val atlas by asset("atlas.json") + val atlas by asset("map.atlas") val music by asset("mapScreen.ogg") } val mapScreenAssets = MapScreenAssets(manager) // Then, when ready to load them: -// Using asset() to create the assets pre-queues them once the group is instantiated. -// If you were using delayedAsset() instead, you would call mapScreenAssets.loadAll() when ready to queue them. +mapScreenAssets.loadALL() if (mapScreenAssets.update()) { // The member assets are now ready to use. } else { // Continue showing loading screen, for example. } -// Alternatively, mapScreenAsset.finishLoading() would block until the assets are loaded, possibly finishing earlier -// than manager.finishLoading() would. - // When finished with these assets: +uiAssets.unloadAll() mapScreenAssets.unloadAll() ``` From c268c87f028143efd4339871409f9d10482b7d6f Mon Sep 17 00:00:00 2001 From: dakeese Date: Thu, 28 Nov 2019 22:07:45 -0500 Subject: [PATCH 18/45] Improve finishLoading() --- assets/src/main/kotlin/ktx/assets/assets.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/assets/src/main/kotlin/ktx/assets/assets.kt b/assets/src/main/kotlin/ktx/assets/assets.kt index 663ca8dd..2e23997c 100644 --- a/assets/src/main/kotlin/ktx/assets/assets.kt +++ b/assets/src/main/kotlin/ktx/assets/assets.kt @@ -22,6 +22,9 @@ interface Asset { */ val asset: Type + /** The [AssetDescriptor] used by [AssetManager] to identify and load the asset. */ + val assetDescriptor: AssetDescriptor + /** * @return true if the asset is already loaded. If this asset is not loaded on demand and this method returns false, * trying to obtain the [asset] instance might cause an exception. @@ -61,7 +64,7 @@ inline operator fun Asset.getValue(receiver: Any?, property: KPrope * Default implementation of [Asset]. Keeps asset data in an [AssetDescriptor] and delegates asset loading to an * [AssetManager]. Assumes the asset was already scheduled for loading. */ -class ManagedAsset(val manager: AssetManager, val assetDescriptor: AssetDescriptor) : Asset { +class ManagedAsset(val manager: AssetManager, override val assetDescriptor: AssetDescriptor) : Asset { override val asset: Type get() = manager[assetDescriptor] @@ -81,7 +84,7 @@ class ManagedAsset(val manager: AssetManager, val assetDescriptor: AssetDe * so it is advised to load eager assets with another [AssetManager] instance or use them after all regular assets are * already loaded. */ -class DelayedAsset(val manager: AssetManager, val assetDescriptor: AssetDescriptor) : Asset { +class DelayedAsset(val manager: AssetManager, override val assetDescriptor: AssetDescriptor) : Asset { override val asset: Type get() { if (!isLoaded()) finishLoading() @@ -277,8 +280,8 @@ abstract class AssetGroup(val manager: AssetManager, protected val filePrefix: S /** Blocks until all members of this group are loaded. This does not prioritize this group's members, so assets from * outside this group may be loaded as well. */ fun finishLoading() { - while (!isLoaded()) { - if (manager.update()) break + for (member in members) { + manager.finishLoadingAsset(member.assetDescriptor) } } From dd7b3fe990abbe8a41f771c3c363ce3c34eacc0a Mon Sep 17 00:00:00 2001 From: MJ Date: Fri, 29 Nov 2019 22:27:34 +0100 Subject: [PATCH 19/45] Improved documentation of the AssetGroup. #222 --- assets/README.md | 89 ++++++++++--------- assets/src/main/kotlin/ktx/assets/assets.kt | 3 +- .../src/test/kotlin/ktx/assets/assetsTest.kt | 3 +- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/assets/README.md b/assets/README.md index b6437c48..9b586504 100644 --- a/assets/README.md +++ b/assets/README.md @@ -59,47 +59,9 @@ exception thrown during reloading. Note that `AssetManager` can throw `GdxRuntim - `AssetManager.getLoader` and `setLoader` extension methods with reified types added to ease handling of `AssetLoader` instances registered in the `AssetManager`. - The `AssetGroup` class is provided for easily grouping together assets so they can be managed as a group through calls -such as `loadAll()` or `unloadAll()`. The intended use is to subclass `AssetGroup` and -list its member assets as properties using `AssetGroup.asset()` or `AssetGroup.delayedAsset()`. It also allows for using -a common prefix for the file names of the group in case they are stored in a specific subdirectory. For example: -```Kotlin -/** An AssetGroup intended for instantiating at the time it should start being loaded, or an AssetGroup that does not - * share its AssetManager with anything else. The assets are specified by using asset() so they are immediately - * queued with the AssetManager when the class is instantiated. */ -class UIAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "ui/"){ - val skin by asset("skin.json") - val clickSound by asset("mapScreen.wav") -} - -val uiAssets = UIAssets(manager) -// No need to queue them. They are pre-queued by instantiating the class. -uiAssets.finishLoading() // Block until they are finished loading. Incremental loading with update() is also available. - -/** An AssetGroup intended for instantiating before its members are needed (possibly referenced among other AssetGroups - * as non-nullable properties). The assets are specified using delayedAsset() so they are not queued for loading until - * loadAll() is called on the group. */ -class MapScreenAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "mapScreen/"){ - val atlas by asset("map.atlas") - val music by asset("mapScreen.ogg") -} - -val mapScreenAssets = MapScreenAssets(manager) - -// Then, when ready to load them: -mapScreenAssets.loadALL() -if (mapScreenAssets.update()) { - // The member assets are now ready to use. -} else { - // Continue showing loading screen, for example. -} - -// When finished with these assets: -uiAssets.unloadAll() -mapScreenAssets.unloadAll() -``` - -Note: if you can use coroutines in your project, [`ktx-assets-async`](../assets-async) module provides a lightweight -coroutines-based alternative to `AssetManager` that can greatly simplify your asset loading code. +such as `loadAll()` or `unloadAll()`. The intended use is to subclass `AssetGroup` and list its member assets as +properties using `AssetGroup.asset()` or `AssetGroup.delayedAsset()`. It also allows for using a common prefix for +the file names of the group in case they are stored in a specific subdirectory. #### `Disposable` @@ -311,6 +273,51 @@ class MyApp : ApplicationAdapter() { } ``` +Using `AssetGroup` to schedule loading and manage a group of assets: + +```Kotlin +/** An AssetGroup intended for instantiating at the time it should start being loaded, or an AssetGroup that does not + * share its AssetManager with anything else. The assets are specified by using asset() so they are immediately + * queued with the AssetManager when the class is instantiated. */ +class UIAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "ui/"){ + val skin by asset("skin.json") + val clickSound by asset("mapScreen.wav") +} + +val uiAssets = UIAssets(manager) +// No need to queue them. They are pre-queued by instantiating the class. +uiAssets.finishLoading() // Block until they are finished loading. Incremental loading with update() is also available. +// Accessing assets - same as with regular properties: +uiAssets.skin +// Disposing of the assets: +uiAssets.unloadAll() +``` + +Using `AssetGroup` with assets loaded on demand once needed: + +```Kotlin +/** An AssetGroup intended for instantiating before its members are needed (possibly referenced among other AssetGroups + * as non-nullable properties). The assets are specified using delayedAsset() so they are not queued for loading until + * loadAll() is called on the group or any of the assets is accessed. */ +class MapScreenAssets(manager: AssetManager) : AssetGroup(manager, filePrefix = "mapScreen/"){ + val atlas by delayedAsset("map.atlas") + val music by delayedAsset("mapScreen.ogg") +} + +val mapScreenAssets = MapScreenAssets(manager) + +// Then, when ready to load them: +mapScreenAssets.loadAll() +if (mapScreenAssets.update()) { + // The member assets are now ready to use. +} else { + // Continue showing loading screen, for example. +} + +// When finished with these assets: +mapScreenAssets.unloadAll() +``` + #### Implementation tip: type-safe assets Create an enum with all assets of the selected type. Let's assume our application stores all images in `assets/images` diff --git a/assets/src/main/kotlin/ktx/assets/assets.kt b/assets/src/main/kotlin/ktx/assets/assets.kt index 2e23997c..f0a540cf 100644 --- a/assets/src/main/kotlin/ktx/assets/assets.kt +++ b/assets/src/main/kotlin/ktx/assets/assets.kt @@ -309,5 +309,4 @@ abstract class AssetGroup(val manager: AssetManager, protected val filePrefix: S * */ protected inline fun delayedAsset(fileName: String, params: AssetLoaderParameters? = null) = manager.loadOnDemand("$filePrefix$fileName", params).also { members.add(it) } - -} \ No newline at end of file +} diff --git a/assets/src/test/kotlin/ktx/assets/assetsTest.kt b/assets/src/test/kotlin/ktx/assets/assetsTest.kt index e18ed5a4..89fa9741 100644 --- a/assets/src/test/kotlin/ktx/assets/assetsTest.kt +++ b/assets/src/test/kotlin/ktx/assets/assetsTest.kt @@ -559,5 +559,4 @@ class MockAssetLoader(fileHandleResolver: FileHandleResolver) : /** Allows to set [MockAsset.additional] via loader. Tests assets parameters API. */ class MockParameter(val additional: String?) : AssetLoaderParameters() - -} \ No newline at end of file +} From 199c39f8145f3601cf5cf1f4be107fe64dbcb046 Mon Sep 17 00:00:00 2001 From: MJ Date: Fri, 29 Nov 2019 22:30:45 +0100 Subject: [PATCH 20/45] Updated changelog and contributors. #222 --- .github/CONTRIBUTORS.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/CONTRIBUTORS.md b/.github/CONTRIBUTORS.md index 72406a25..5daf38c5 100644 --- a/.github/CONTRIBUTORS.md +++ b/.github/CONTRIBUTORS.md @@ -51,6 +51,7 @@ Project contributors listed chronologically. * Contributed documentation fix. * [@cypherdare](https://github.com/cypherdare) * Improved [actors](../actors) and [graphics](../graphics) utilities. + * Added `AssetGroup` API to [assets](../assets). * [@jakewilson](https://github.com/jakewilson) * Added utilities to [graphics module](../graphics). * [@rhutsaliuk](https://github.com/rhutsaliuk) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c230380..028ac77d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ #### 1.9.10-SNAPSHOT +- **[FEATURE]** (`ktx-assets`) Added `AssetGroup` abstract class that allows to manage groups of assets. - **[FEATURE]** (`ktx-graphics`) Added `Batch.begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4`. - **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. - **[FEATURE]** (`ktx-style`) The overloaded `+=` operator can now be used to add `"default"` resources to `Skin`. From a65c6702309c1e05f016f0cbeb613b8ea2e50184 Mon Sep 17 00:00:00 2001 From: dakeese Date: Mon, 9 Dec 2019 21:32:27 -0500 Subject: [PATCH 21/45] Add GdxArray removeAll/retainAll. --- collections/README.md | 3 +- .../src/main/kotlin/ktx/collections/arrays.kt | 34 +++++++++++++++++++ .../test/kotlin/ktx/collections/arraysTest.kt | 20 ++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/collections/README.md b/collections/README.md index 585d2da9..1599db3d 100644 --- a/collections/README.md +++ b/collections/README.md @@ -32,7 +32,8 @@ chained. - Missing `addAll` and `removeAll` methods for arrays and iterables were added. - `iterate` method allows to iterate over collection's elements, while providing reference to `MutableInterator`. Can be used to easily remove collection elements during iteration. -- `map`, `filter`, `flatten` and `flatMap` methods that work like methods in Kotlin stdlib but return `GdxArray`. +- `removeAll` and `retainAll` higher order functions that work like functions in Kotlin stdlib +- `map`, `filter`, `flatten` and `flatMap` functions that work like functions in Kotlin stdlib but return `GdxArray`. - Every iterable and array can be converted to `Array` using `toGdxArray` method. - `IntArray`, `BooleanArray` and `FloatArray` can be converted to corresponding LibGDX primitive collections using `toGdxArray` method. diff --git a/collections/src/main/kotlin/ktx/collections/arrays.kt b/collections/src/main/kotlin/ktx/collections/arrays.kt index cef81421..42dac39b 100644 --- a/collections/src/main/kotlin/ktx/collections/arrays.kt +++ b/collections/src/main/kotlin/ktx/collections/arrays.kt @@ -225,6 +225,40 @@ inline fun > GdxArray.sortByDescending(crossin if (size > 1) this.sort(compareByDescending(selector)) } +/** + * Removes elements from the array that satisfy the predicate. + */ +inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { + var currentWriteIndex = 0 + for (i in 0 until size){ + val value = this[i] + if (!predicate(value)){ + if (currentWriteIndex != i){ + this[currentWriteIndex] = value + } + currentWriteIndex++ + } + } + truncate(currentWriteIndex) +} + +/** + * Removes elements from the array that do not satisfy the predicate. + */ +inline fun GdxArray.retainAll(predicate: (Type) -> Boolean) { + var currentWriteIndex = 0 + for (i in 0 until size){ + val value = this[i] + if (predicate(value)){ + if (currentWriteIndex != i){ + this[currentWriteIndex] = value + } + currentWriteIndex++ + } + } + truncate(currentWriteIndex) +} + /** * Returns a [GdxArray] containing the results of applying the given [transform] function * to each element in the original [GdxArray]. diff --git a/collections/src/test/kotlin/ktx/collections/arraysTest.kt b/collections/src/test/kotlin/ktx/collections/arraysTest.kt index 317dd1f1..ecfd1dd6 100644 --- a/collections/src/test/kotlin/ktx/collections/arraysTest.kt +++ b/collections/src/test/kotlin/ktx/collections/arraysTest.kt @@ -364,6 +364,25 @@ class ArraysTest { assertEquals(GdxArray.with("Twenty-one", "Eleven", "One"), array) } + @Test + fun `should remove elements from existing GdxArray`() { + val array = GdxArray.with(1, 2, 3, 4, 5) + + array.removeAll { it % 2 == 0 } + + assertEquals(GdxArray.with(1, 3, 5), array) + } + + @Test + fun `should retain elements from existing GdxArray`() { + val array = GdxArray.with(1, 2, 3, 4, 5) + + array.retainAll { it % 2 == 1 } + + assertEquals(GdxArray.with(1, 3, 5), array) + } + + @Test fun `should map elements into a new GdxArray`() { val array = GdxArray.with(1, 2, 3) @@ -372,7 +391,6 @@ class ArraysTest { assertEquals(GdxArray.with(2, 4, 6), result) } - @Test fun `should filter elements into a new GdxArray`() { val array = GdxArray.with(1, 2, 3, 4, 5) From f5d0128f16374916b5cd1c671a1d3f6e5f0b406a Mon Sep 17 00:00:00 2001 From: dakeese Date: Mon, 9 Dec 2019 21:32:50 -0500 Subject: [PATCH 22/45] Mark PooledList as a MutableIterable --- collections/src/main/kotlin/ktx/collections/lists.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/collections/src/main/kotlin/ktx/collections/lists.kt b/collections/src/main/kotlin/ktx/collections/lists.kt index 5753366a..67cc5d7f 100644 --- a/collections/src/main/kotlin/ktx/collections/lists.kt +++ b/collections/src/main/kotlin/ktx/collections/lists.kt @@ -26,7 +26,7 @@ typealias GdxList = PooledList * * @param nodePool provides and manages [Node] instances. */ -class PooledList(val nodePool: Pool>) : Iterable { +class PooledList(val nodePool: Pool>) : MutableIterable { /** * Current amount of elements in this list. */ @@ -306,7 +306,7 @@ class PooledList(val nodePool: Pool>) : Iterable { * @see insertBefore * @see insertAfter */ - class PooledListIterator(var list: PooledList) : MutableIterator, Iterable { + class PooledListIterator(var list: PooledList) : MutableIterator, MutableIterable { internal var current = list.main var reversed = false @@ -344,7 +344,7 @@ class PooledList(val nodePool: Pool>) : Iterable { /** * @return this instance of [PooledList] iterator. */ - override fun iterator(): Iterator = this + override fun iterator(): MutableIterator = this } // Internal API. From 94ea5a17b05df40a40763f4150227ca6934dda4b Mon Sep 17 00:00:00 2001 From: dakeese Date: Wed, 11 Dec 2019 21:01:16 -0500 Subject: [PATCH 23/45] Add edge case tests; fix formatting --- CHANGELOG.md | 2 ++ collections/README.md | 4 ++-- .../src/main/kotlin/ktx/collections/arrays.kt | 20 +++++++++---------- .../test/kotlin/ktx/collections/arraysTest.kt | 19 +++++++++++++++--- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 028ac77d..d3cec64d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ #### 1.9.10-SNAPSHOT - **[FEATURE]** (`ktx-assets`) Added `AssetGroup` abstract class that allows to manage groups of assets. +- **[FEATURE]** (`ktx-collections`) Added `Array.removeAll` and `Array.retainAll` higher-order in-place filters. +- **[FEATURE]** (`ktx-collections`) Made PooledList a MutableIterable. - **[FEATURE]** (`ktx-graphics`) Added `Batch.begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4`. - **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. - **[FEATURE]** (`ktx-style`) The overloaded `+=` operator can now be used to add `"default"` resources to `Skin`. diff --git a/collections/README.md b/collections/README.md index 1599db3d..0573988c 100644 --- a/collections/README.md +++ b/collections/README.md @@ -32,8 +32,8 @@ chained. - Missing `addAll` and `removeAll` methods for arrays and iterables were added. - `iterate` method allows to iterate over collection's elements, while providing reference to `MutableInterator`. Can be used to easily remove collection elements during iteration. -- `removeAll` and `retainAll` higher order functions that work like functions in Kotlin stdlib -- `map`, `filter`, `flatten` and `flatMap` functions that work like functions in Kotlin stdlib but return `GdxArray`. +- `removeAll` and `retainAll` higher-order functions that work like functions in Kotlin stdlib +- `map`, `filter`, `flatten` and `flatMap` methods that work like methods in Kotlin stdlib but return `GdxArray`. - Every iterable and array can be converted to `Array` using `toGdxArray` method. - `IntArray`, `BooleanArray` and `FloatArray` can be converted to corresponding LibGDX primitive collections using `toGdxArray` method. diff --git a/collections/src/main/kotlin/ktx/collections/arrays.kt b/collections/src/main/kotlin/ktx/collections/arrays.kt index 42dac39b..40c50dad 100644 --- a/collections/src/main/kotlin/ktx/collections/arrays.kt +++ b/collections/src/main/kotlin/ktx/collections/arrays.kt @@ -230,11 +230,11 @@ inline fun > GdxArray.sortByDescending(crossin */ inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { var currentWriteIndex = 0 - for (i in 0 until size){ - val value = this[i] - if (!predicate(value)){ - if (currentWriteIndex != i){ - this[currentWriteIndex] = value + for (i in 0 until size) { + val value = items[i] + if (!predicate(value)) { + if (currentWriteIndex != i) { + items[currentWriteIndex] = value } currentWriteIndex++ } @@ -247,11 +247,11 @@ inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { */ inline fun GdxArray.retainAll(predicate: (Type) -> Boolean) { var currentWriteIndex = 0 - for (i in 0 until size){ - val value = this[i] - if (predicate(value)){ - if (currentWriteIndex != i){ - this[currentWriteIndex] = value + for (i in 0 until size) { + val value = items[i] + if (predicate(value)) { + if (currentWriteIndex != i) { + items[currentWriteIndex] = value } currentWriteIndex++ } diff --git a/collections/src/test/kotlin/ktx/collections/arraysTest.kt b/collections/src/test/kotlin/ktx/collections/arraysTest.kt index ecfd1dd6..08d58383 100644 --- a/collections/src/test/kotlin/ktx/collections/arraysTest.kt +++ b/collections/src/test/kotlin/ktx/collections/arraysTest.kt @@ -367,21 +367,34 @@ class ArraysTest { @Test fun `should remove elements from existing GdxArray`() { val array = GdxArray.with(1, 2, 3, 4, 5) + array.removeAll { it > 10 } + assertEquals(GdxArray.with(1, 2, 3, 4, 5), array) array.removeAll { it % 2 == 0 } - assertEquals(GdxArray.with(1, 3, 5), array) + + array.removeAll { it is Number } + assertEquals(GdxArray(), array) + + array.removeAll { it > 0 } + assertEquals(GdxArray(), array) } @Test fun `should retain elements from existing GdxArray`() { val array = GdxArray.with(1, 2, 3, 4, 5) + array.retainAll { it < 6 } + assertEquals(GdxArray.with(1, 2, 3, 4, 5), array) array.retainAll { it % 2 == 1 } - assertEquals(GdxArray.with(1, 3, 5), array) - } + array.retainAll { it < 0 } + assertEquals(GdxArray(), array) + + array.retainAll { it > 0 } + assertEquals(GdxArray(), array) + } @Test fun `should map elements into a new GdxArray`() { From a442c3753f34519fc8bb52cebfa21f4c9ff038a3 Mon Sep 17 00:00:00 2001 From: MJ Date: Fri, 13 Dec 2019 02:16:09 +0100 Subject: [PATCH 24/45] Updated ktx-collection documentation. #226 --- .github/CONTRIBUTORS.md | 2 +- CHANGELOG.md | 2 +- collections/README.md | 3 +-- collections/src/main/kotlin/ktx/collections/arrays.kt | 10 ++++++++-- .../src/test/kotlin/ktx/collections/arraysTest.kt | 1 + 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/CONTRIBUTORS.md b/.github/CONTRIBUTORS.md index 5daf38c5..b2aa670d 100644 --- a/.github/CONTRIBUTORS.md +++ b/.github/CONTRIBUTORS.md @@ -50,7 +50,7 @@ Project contributors listed chronologically. * [@kvonspiczak](https://github.com/kvonspiczak) * Contributed documentation fix. * [@cypherdare](https://github.com/cypherdare) - * Improved [actors](../actors) and [graphics](../graphics) utilities. + * Contributed to numerous utilities in [actors](../actors), [collections](../collections) and [graphics](../graphics) modules. * Added `AssetGroup` API to [assets](../assets). * [@jakewilson](https://github.com/jakewilson) * Added utilities to [graphics module](../graphics). diff --git a/CHANGELOG.md b/CHANGELOG.md index d3cec64d..0d8008da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ - **[FEATURE]** (`ktx-assets`) Added `AssetGroup` abstract class that allows to manage groups of assets. - **[FEATURE]** (`ktx-collections`) Added `Array.removeAll` and `Array.retainAll` higher-order in-place filters. -- **[FEATURE]** (`ktx-collections`) Made PooledList a MutableIterable. +- **[CHANGE]** (`ktx-collections`) `PooledList` now implements `MutableIterable`. - **[FEATURE]** (`ktx-graphics`) Added `Batch.begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4`. - **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. - **[FEATURE]** (`ktx-style`) The overloaded `+=` operator can now be used to add `"default"` resources to `Skin`. diff --git a/collections/README.md b/collections/README.md index 0573988c..82b368dd 100644 --- a/collections/README.md +++ b/collections/README.md @@ -32,7 +32,7 @@ chained. - Missing `addAll` and `removeAll` methods for arrays and iterables were added. - `iterate` method allows to iterate over collection's elements, while providing reference to `MutableInterator`. Can be used to easily remove collection elements during iteration. -- `removeAll` and `retainAll` higher-order functions that work like functions in Kotlin stdlib +- `removeAll` and `retainAll` higher-order functions that work like collection extensions in Kotlin stdlib. - `map`, `filter`, `flatten` and `flatMap` methods that work like methods in Kotlin stdlib but return `GdxArray`. - Every iterable and array can be converted to `Array` using `toGdxArray` method. - `IntArray`, `BooleanArray` and `FloatArray` can be converted to corresponding LibGDX primitive collections using @@ -186,4 +186,3 @@ collections are not - Koloboke maps and sets can fully benefit from Kotlin stand #### Additional documentation - [LibGDX collections article.](https://github.com/libgdx/libgdx/wiki/Collections) - diff --git a/collections/src/main/kotlin/ktx/collections/arrays.kt b/collections/src/main/kotlin/ktx/collections/arrays.kt index 40c50dad..be2d9428 100644 --- a/collections/src/main/kotlin/ktx/collections/arrays.kt +++ b/collections/src/main/kotlin/ktx/collections/arrays.kt @@ -4,16 +4,22 @@ package ktx.collections /** Alias for [com.badlogic.gdx.utils.Array] avoiding name collision with the standard library. */ typealias GdxArray = com.badlogic.gdx.utils.Array + /** Alias for [com.badlogic.gdx.utils.BooleanArray] avoiding name collision with the standard library. */ typealias GdxBooleanArray = com.badlogic.gdx.utils.BooleanArray + /** Alias for [com.badlogic.gdx.utils.FloatArray] avoiding name collision with the standard library. */ typealias GdxFloatArray = com.badlogic.gdx.utils.FloatArray + /** Alias for [com.badlogic.gdx.utils.IntArray] avoiding name collision with the standard library. */ typealias GdxIntArray = com.badlogic.gdx.utils.IntArray + /** Alias for [com.badlogic.gdx.utils.CharArray] avoiding name collision with the standard library. */ typealias GdxCharArray = com.badlogic.gdx.utils.CharArray + /** Alias for [com.badlogic.gdx.utils.LongArray] avoiding name collision with the standard library. */ typealias GdxLongArray = com.badlogic.gdx.utils.LongArray + /** Alias for [com.badlogic.gdx.utils.ShortArray] avoiding name collision with the standard library. */ typealias GdxShortArray = com.badlogic.gdx.utils.ShortArray @@ -226,7 +232,7 @@ inline fun > GdxArray.sortByDescending(crossin } /** - * Removes elements from the array that satisfy the predicate. + * Removes elements from the array that satisfy the [predicate]. */ inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { var currentWriteIndex = 0 @@ -243,7 +249,7 @@ inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { } /** - * Removes elements from the array that do not satisfy the predicate. + * Removes elements from the array that do not satisfy the [predicate]. */ inline fun GdxArray.retainAll(predicate: (Type) -> Boolean) { var currentWriteIndex = 0 diff --git a/collections/src/test/kotlin/ktx/collections/arraysTest.kt b/collections/src/test/kotlin/ktx/collections/arraysTest.kt index 08d58383..9bdd419e 100644 --- a/collections/src/test/kotlin/ktx/collections/arraysTest.kt +++ b/collections/src/test/kotlin/ktx/collections/arraysTest.kt @@ -404,6 +404,7 @@ class ArraysTest { assertEquals(GdxArray.with(2, 4, 6), result) } + @Test fun `should filter elements into a new GdxArray`() { val array = GdxArray.with(1, 2, 3, 4, 5) From 26d8d10fb9c2c6d8a66e77bf86b08dac8bfcc444 Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 16 Dec 2019 09:58:08 +0100 Subject: [PATCH 25/45] Listed all ktx-style extensions. #225 --- style/README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/style/README.md b/style/README.md index 44aa9461..56459376 100644 --- a/style/README.md +++ b/style/README.md @@ -49,6 +49,16 @@ skin.remove("name") val map: ObjectMap? = skin.getAll() ``` +These extension methods and operators include: +* `get` (square bracket operator): returns a resource from the `Skin` or throws an exception. +* `optional`: returns `null` or a resource from the `Skin` if it exists. +* `set` (square bracket operator): assigns a resource to the `Skin`. +* `add`: assigns a resource to the `Skin`. +* `plusAssign` (`+=` operator): assigns a resource with the default style to the `Skin`. +* `remove`: removes a resource from the `Skin`. +* `has`: checks is the `Skin` contains a resource. +* `getAll`: returns all resources of the selected type. + Note that all `name` parameters can also be skipped to use the default style name, `"default"`. An extension method for every style of every Scene2D widget was added to `Skin`. Each method name matches `lowerCamelCase` @@ -57,6 +67,29 @@ Signature of every extension method is pretty much the same - they consume 3 par `"default"`), optional name of extended style and an init block, which is usually passed as a Kotlin lambda. If a name of existing style name is given as the `extend` parameter, the new style will copy its properties. +Currently supported extension methods include: + +`Skin` method | Style class +:---: | --- +`color` | `com.badlogic.gdx.graphics.Color` +`button` | `com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle` +`checkBox` | `com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckBoxStyle` +`imageButton` | `com.badlogic.gdx.scenes.scene2d.ui.ImageButton.ImageButtonStyle` +`imageTextButton` | `com.badlogic.gdx.scenes.scene2d.ui.ImageTextButton.ImageTextButtonStyle` +`label` | `com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle` +`list` | `com.badlogic.gdx.scenes.scene2d.ui.List.ListStyle` +`progressBar` | `com.badlogic.gdx.scenes.scene2d.ui.ProgressBar.ProgressBarStyle` +`scrollPane` | `com.badlogic.gdx.scenes.scene2d.ui.ScrollPane.ScrollPaneStyle` +`selectBox` | `com.badlogic.gdx.scenes.scene2d.ui.SelectBox.SelectBoxStyle` +`slider` | `com.badlogic.gdx.scenes.scene2d.ui.Slider.SliderStyle` +`splitPaneStyle` | `com.badlogic.gdx.scenes.scene2d.ui.SplitPane.SplitPaneStyle` +`textButton` | `com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle` +`textField` | `com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle` +`textTooltip` | `com.badlogic.gdx.scenes.scene2d.ui.TextTooltip.TextTooltipStyle` +`touchpad` | `com.badlogic.gdx.scenes.scene2d.ui.Touchpad.TouchpadStyle` +`tree` | `com.badlogic.gdx.scenes.scene2d.ui.Tree.TreeStyle` +`window` | `com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle` + ### Usage examples Creating a new empty `Skin`: @@ -80,6 +113,27 @@ val skin = skin(TextureAtlas(Gdx.files.internal("skin.atlas"))) { } ``` +Extending an existing `Skin`: +```Kotlin +import com.badlogic.gdx.scenes.scene2d.ui.Skin +import ktx.style.* + +val skin = Skin() + +// All style builders are regular extension methods, +// so they can be used directly on a `Skin` instance: +skin.label { + // Define your label style here. +} + +// Style definitions can also be wrapped in a block such as apply: +skin.apply { + button { + // Define your button style here. + } +} +``` + Creating a new `LabelStyle` with `"default"` name: ```Kotlin import com.badlogic.gdx.graphics.Color From c0420246f7d0c8c0f1e9913e6a284b9d32eb4dcb Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 16 Dec 2019 10:08:20 +0100 Subject: [PATCH 26/45] Mentioned ktx-style in skin management section. #225 --- scene2d/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scene2d/README.md b/scene2d/README.md index 40ec7c8f..74efae92 100644 --- a/scene2d/README.md +++ b/scene2d/README.md @@ -152,6 +152,8 @@ table { } ``` +When constructing `Skin` instances and defining styles for your UI, [`ktx-style`](../style) module might prove useful. + #### `KWidgets` To avoid method collisions and ease the implementation of type-safe builders, most so-called "parental" widgets (extending From ee56f8271153fc617010d7ffc88f6fe8a7a04de6 Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 16 Dec 2019 10:12:09 +0100 Subject: [PATCH 27/45] Mentioned ktx-vis-style in extra libraries section. #225 --- vis/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vis/README.md b/vis/README.md index ebd37367..91d5d6ac 100644 --- a/vis/README.md +++ b/vis/README.md @@ -31,8 +31,9 @@ builders. #### Additional extensions -Consider using [ktx-actors](../actors) module to improve event handling with lambda-friendly extension methods like -`onChange` and `onClick`. +Consider using [`ktx-actors`](../actors) module to improve event handling with lambda-friendly extension methods like +`onChange` and `onClick`. [`ktx-assets`](../assets) might help with UI resources management, while +[`ktx-vis-style`](../vis-style) adds DSL for defining custom VisUI widgets. #### Note about `KWidgets` From db43115fc9f857d37bcff0583344029f0151ff0f Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 16 Dec 2019 10:37:45 +0100 Subject: [PATCH 28/45] Extended ktx-vis-style documentation. #225 --- vis-style/README.md | 74 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/vis-style/README.md b/vis-style/README.md index 5878aa0b..a0b8d909 100644 --- a/vis-style/README.md +++ b/vis-style/README.md @@ -5,12 +5,76 @@ Type-safe builders of **VisUI** widget styles. This is an extension of [`ktx-style`](../style) module. See its documentation to get started with type-safe stylesheet builders for `Scene2D` widgets. -Additionally to features provided by the `ktx-style` library, `ktx-style-vis` comes with factory methods for most -[VisUI](https://github.com/kotcrab/vis-editor/wiki/VisUI) widget styles. +Additionally to features provided by the `ktx-style` library, `ktx-vis-style` comes with factory methods for most +[VisUI](https://github.com/kotcrab/vis-editor/wiki/VisUI) widget styles. On top of original Scene2D widget styles +handled by basic `ktx-style` utilities, supported extension methods include: -_Implementation note_: `FileChooserStyle` is not included, as it is basically a desktop-only widget that would not even -work (or compile - see GWT) on most platforms. Adding a similar extension method for `FileChooserStyle` would be pretty -straightforward: see `visStyle.kt` for code samples. +`Skin` method | Style class +:---: | --- +`sizes` | `com.kotcrab.vis.ui.Sizes` +`busyBar` | `com.kotcrab.vis.ui.widget.BusyBar.BusyBarStyle` +`colorPicker` | `com.kotcrab.vis.ui.widget.color.ColorPickerStyle` +`colorPickerWidget` | `com.kotcrab.vis.ui.widget.color.ColorPickerWidgetStyle` +`formValidator` | `com.kotcrab.vis.ui.util.form.SimpleFormValidator.FormValidatorStyle` +`linkLabel` | `com.kotcrab.vis.ui.widget.LinkLabel.LinkLabelStyle` +`listView` | `com.kotcrab.vis.ui.widget.ListViewStyle` +`menu` | `com.kotcrab.vis.ui.widget.Menu.MenuStyle` +`menuBar` | `com.kotcrab.vis.ui.widget.MenuBar.MenuBarStyle` +`menuItem` | `com.kotcrab.vis.ui.widget.MenuItem.MenuItemStyle` +`multiSplitPane` | `com.kotcrab.vis.ui.widget.MultiSplitPane.MultiSplitPaneStyle` +`popupMenu` | `com.kotcrab.vis.ui.widget.PopupMenu.PopupMenuStyle` +`separator` | `com.kotcrab.vis.ui.widget.Separator.SeparatorStyle` +`simpleListAdapter` | `com.kotcrab.vis.ui.util.adapter.SimpleListAdapter.SimpleListAdapterStyle` +`spinner` | `com.kotcrab.vis.ui.widget.spinner.Spinner.SpinnerStyle` +`tabbedPane` | `com.kotcrab.vis.ui.widget.tabbedpane.TabbedPane.TabbedPaneStyle` +`toast` | `com.kotcrab.vis.ui.widget.toast.Toast.ToastStyle` +`visCheckBox` | `com.kotcrab.vis.ui.widget.VisCheckBox.VisCheckBoxStyle` +`visImageButton` | `com.kotcrab.vis.ui.widget.VisImageButton.VisImageButtonStyle` +`visImageTextButton` | `com.kotcrab.vis.ui.widget.VisImageTextButton.VisImageTextButtonStyle` +`visSplitPane` | `com.kotcrab.vis.ui.widget.VisSplitPane.VisSplitPaneStyle` +`visTextButton` | `com.kotcrab.vis.ui.widget.VisTextButton.VisTextButtonStyle` +`visTextField` | `com.kotcrab.vis.ui.widget.VisTextField.VisTextFieldStyle` +`visTooltip` | `com.kotcrab.vis.ui.widget.Tooltip.TooltipStyle` + +_Note_: `FileChooserStyle` is not included, as it is basically a desktop-only widget that does not work +(or compile - see GWT) on most platforms. Adding a similar extension method for `FileChooserStyle` would be pretty +straightforward: see [`visStyle.kt`](src/main/kotlin/ktx/style/visStyle.kt) for code samples. + +### Guide + +Additionally to providing a significant amount of new widgets, VisUI also provides a default skin +style following a modern flat design. Until you decide to redesign the UI completely, extending +the defaults is the common thing to do. + +Extending the default VisUI `Skin`: + +```kotlin +import com.badlogic.gdx.scenes.scene2d.ui.Skin +import com.kotcrab.vis.ui.VisUI +import com.kotcrab.vis.ui.VisUI.SkinScale.X1 +import ktx.style.defaultStyle +import ktx.style.visCheckBox + +fun loadSkin(): Skin { + VisUI.load(X1) + val skin: Skin = VisUI.getSkin() + return skin.apply { + visCheckBox(extend = defaultStyle) { + // Define your extended style here. + } + } +} +``` + +To see all styles provided by default, refer to the +[JSON `Skin` definition](https://github.com/kotcrab/vis-ui/blob/master/ui/src/main/resources/com/kotcrab/vis/ui/skin/x1/uiskin.json) +in VisUI library. Additional skin styles can also be found in projects such as +[vis-ui-contrib](https://github.com/kotcrab/vis-ui-contrib) or +[gdx-skins](https://github.com/czyzby/gdx-skins). + +Other than providing additional extension methods, `ktx-vis-style` works very similarly to the +original [`ktx-style`](../style) module. Please refer to its guide for further details on how +to use this library. #### Additional documentation From 6f659b9f8b3c8c8d90d5e8befbe6d948306272b2 Mon Sep 17 00:00:00 2001 From: MJ Date: Mon, 16 Dec 2019 11:10:06 +0100 Subject: [PATCH 29/45] Updated to Kotlin 1.3.61 and Coroutines 1.3.3. #229 --- CHANGELOG.md | 2 ++ README.md | 7 ++----- async/README.md | 2 +- gradle.properties | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d8008da..5c500c94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ #### 1.9.10-SNAPSHOT +- **[UPDATE]** Updated to Kotlin 1.3.61. +- **[UPDATE]** Updated to Kotlin Coroutines 1.3.3. - **[FEATURE]** (`ktx-assets`) Added `AssetGroup` abstract class that allows to manage groups of assets. - **[FEATURE]** (`ktx-collections`) Added `Array.removeAll` and `Array.retainAll` higher-order in-place filters. - **[CHANGE]** (`ktx-collections`) `PooledList` now implements `MutableIterable`. diff --git a/README.md b/README.md index 9b2ca7bc..d02493dc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Travis CI](https://travis-ci.org/libktx/ktx.svg?branch=master)](https://travis-ci.org/libktx/ktx) [![Maven Central](https://img.shields.io/maven-central/v/io.github.libktx/ktx-async.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22) -[![Kotlin](https://img.shields.io/badge/kotlin-1.3.50-orange.svg)](http://kotlinlang.org/) +[![Kotlin](https://img.shields.io/badge/kotlin-1.3.61-orange.svg)](http://kotlinlang.org/) [![LibGDX](https://img.shields.io/badge/libgdx-1.9.10-red.svg)](https://libgdx.badlogicgames.com/) [![KTX](.github/ktx-logo.png "KTX")](http://libktx.github.io) @@ -11,10 +11,7 @@ _**K**o**t**lin utilities for LibGD**X** applications._ **KTX** aims to make [LibGDX](http://libgdx.badlogicgames.com/) as [Kotlin](http://kotlinlang.org/)-friendly as possible without turning the API upside down. It provides modular utilities for certain parts of LibGDX with poor Kotlin support. -This is **not** a new framework by any means - but Kotlin certainly makes LibGDX feel like one. - -Do not confuse **KTX** with [`android-ktx`](https://github.com/android/android-ktx): an official Google project with -Android utilities. "**KTX**" name was chosen long before the Android project was announced. +This is *not* a new framework by any means. ### Modules diff --git a/async/README.md b/async/README.md index 0bf52426..26a0d019 100644 --- a/async/README.md +++ b/async/README.md @@ -1,4 +1,4 @@ -[![Kotlin](https://img.shields.io/badge/kotlin--coroutines-1.3.0-orange.svg)](http://kotlinlang.org/) +[![Kotlin](https://img.shields.io/badge/kotlin--coroutines-1.3.3-orange.svg)](http://kotlinlang.org/) # KTX: coroutines support and threading utilities diff --git a/gradle.properties b/gradle.properties index 33247bc9..d561f260 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ libGroup=io.github.libktx gdxVersion=1.9.10 -kotlinVersion=1.3.50 -kotlinCoroutinesVersion=1.3.0 +kotlinVersion=1.3.61 +kotlinCoroutinesVersion=1.3.3 ashleyVersion=1.7.3 visUiVersion=1.4.4 From 128d3496641d80a01dd166c472664c5ed97aec35 Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 00:33:20 +0100 Subject: [PATCH 30/45] Fixed grammatical error in README. #230 --- inject/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inject/README.md b/inject/README.md index 0bed79e4..cfbd5b7e 100644 --- a/inject/README.md +++ b/inject/README.md @@ -16,7 +16,7 @@ Java dependency injection mechanisms usually rely on annotations and compile-tim its inline functions, allows to omit the reflection and annotations usage altogether, while still providing a pleasant DSL. -Why not use an existing Kotlin DI library? `ktx-inject` is a tiny extension consisting a single source file with a few +Why not use an existing Kotlin DI library? `ktx-inject` is a tiny extension consisting of a single source file with a few hundred lines, most of which are the documentation. Being as lightweight as possible and generating little to no garbage at runtime, it aims to be a viable choice for even the slowest devices out there. It sacrifices extra features for simplicity and nearly zero overhead at runtime. From fed297ed27e65fc903d538668848226a5e399a75 Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 02:26:31 +0100 Subject: [PATCH 31/45] Main documentation files rewrite. #231 --- .github/CONTRIBUTING.md | 95 +++++++++++++++-------- .github/CONTRIBUTORS.md | 10 ++- README.md | 166 ++++++++++++++++++++++++++-------------- 3 files changed, 178 insertions(+), 93 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7a90bda9..90ff61b1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,3 +1,5 @@ +# Contribution guidelines + ## Issues - Before submitting a bug-related issue, make sure that **its source is not the LibGDX itself**. @@ -15,15 +17,18 @@ JUnit and [Spek](http://spekframework.org/) can be used to write tests. Use [Mockito-Kotlin](https://github.com/nhaarman/mockito-kotlin) for mocking. - If your pull request is not a simple bug fix or small utility, make sure to link it to an existing issue or create an issue with your proposal first. Major API changes or new modules have to be discussed with the maintainers first. +Skipping the issue will not get your pull request rejected outright, but note that undiscussed major changes might +require a rewrite. - All notable changes should be added to the [changelog](../CHANGELOG.md) with an appropriate label: - **[FEATURE]** - a new functionality. - - **[CHANGE]** - breaking API change. - - **[UPDATE]** - update of one of the dependencies. - - **[FIX]** - bug fix. + - **[CHANGE]** - an API breaking change. + - **[UPDATE]** - an update of one of the major dependencies. + - **[FIX]** - a bug fix. - **[MISC]** - other changes (e.g. related to documentation or the project itself). - Some libraries (like `ktx-collections` or `ktx-math`) list _all_ features in the `README.md` files to ease their -usage. When adding new feature to these projects, please add description of your change to the file. Also, add all -necessary imports in KTX examples in `README.md` files to make it easier to try them out. +usage. When adding a new feature, please check the `README.md` file of the module and add description of your change +when appropriate. Any major change should include a usage example in the module guide. Also, make sure to add all +of the necessary imports in KTX examples in `README.md` files to make it easier to try them out. ## Working from sources @@ -36,36 +41,52 @@ git checkout develop ### Build tool The project itself is managed by [Gradle](http://gradle.org/). Gradle wrapper is included, but you can use a local -Gradle installation - scripts should be compatible with Gradle `4.+`. If you consider working from sources, these are -some useful Gradle tasks that you can look into: +Gradle installation - scripts should be compatible with Gradle `5.+`. Some useful Gradle tasks include: - `build install` - builds the libraries archives and pushes them to _Maven Local_. - `check` - runs all tests in all projects. -- `clean` - removes `build` directories. +- `clean` - removes the `build` directories, which forces rebuilds of the modules. - `distZip` - prepares a zip archive with all jars in `build/distributions` folder. Useful for releases. -- `uploadArchives` - pushes the archives to _Maven Central_. Requires proper `gradle.properties` with archive signing and -Sonatype logging data. +- `uploadArchives` - pushes the archives to _Maven Central_. Requires proper `gradle.properties` with archive signing +and _Sonatype_ logging data. - `closeAndPromoteRepository` - closes and promotes Nexus repository. Should be run after `uploadArchives` in -case of a non-snapshot upload to _Maven Central_. +case of a non-snapshot upload to _Maven Central_. Might fail at times on the promotion task; running `promoteRepository` +separately usually fixes the issue. ### Versioning and uploading -Releasing a new KTX version: +#### Stable release -- Change `libVersion` settings in `version.txt`. KTX uses the same versioning schema as LibGDX (mimicking the -LibGDX version that it was compiled against) with optional `-b#` or `-SNAPSHOT` suffixes depending on version status. +- Create a new issue on GitHub. Include the number of the issue in commit messages of all commits related to the release. +Apply `misc` label and milestone corresponding to the LibGDX version. An example can be found +[here](https://github.com/libktx/ktx/issues/191). +- Change `libVersion` setting in the [`version.txt`](../version.txt). KTX uses the same versioning schema as LibGDX +(mimicking the LibGDX version that it was compiled against) with optional `-b#` suffix depending on the version status. +- Create a pull request from the `develop` branch to the `master` branch. Review and merge the changes to the `master` +branch. +- Checkout the `master` branch. Fetch the latest changes. - Run `gradle build install uploadArchives closeAndPromoteRepository` to push artifacts to both _Maven Local_ and -_Maven Central_. Note that Maven plugin has its issues and you might need to run `gradle promoteRepository` after the -previous task sequence (if it fails on the `closeAndPromoteRepository` task). -- Run `gradle distZip` to prepare archive with KTX sources, compiled binary and documentation. -- Upload the archive to [releases](https://github.com/libktx/ktx/releases) section. Tag should match released version. -Name of the release should match `KTX $libVersion`. Copy latest [changelog](../CHANGELOG.md) entries to release -description. Note that a release is not necessary for snapshot versions. If there are any known issues with the previous -or current versions, please attach additional _Known issues:_ section with the following labels: +_Maven Central_. Note that the Maven plugin has its issues and you might need to run `gradle promoteRepository` after +the previous task sequence (if it fails on the `closeAndPromoteRepository` task). +- Run `gradle distZip` to prepare an archive with KTX sources, compiled binary and documentation. +- Upload the archive to [releases](https://github.com/libktx/ktx/releases) section. The tag should be made from the +`master` branch and its name should match the released version. Name of the release should match `KTX $libVersion`. +Add a short release summary and copy the latest [changelog](../CHANGELOG.md) entries to the release description. +- If there are any known issues with the previous or current versions, please attach additional _Known issues:_ section +with the following labels: - **[BUG]** - a known bug in the release that is or will be fixed in the following versions. - **[INCOMPATIBILITY]** - incompatibility with one of the previously supported or currently released versions of one of the major dependencies. - **[REMOVAL]** - temporary or permanent removal of a major feature (e.g. disabling a module for a single release). +- Checkout the `develop` branch. +- Change `libVersion` setting in the [`version.txt`](../version.txt) to the next snapshot release. The name should +match the used LibGDX version followed by the `-SNAPSHOT` suffix. + +#### Snapshot release + +- Make sure that the [`version.txt`](../version.txt) ends with the `-SNAPSHOT` suffix and matches the LibGDX version +that the library was compiled against. +- Run `gradle build install uploadArchives` to push artifacts to both _Maven Local_ and _Sonatype_ snapshots repository. ### Updating dependencies @@ -88,11 +109,10 @@ All of the major dependencies updates should be added to the [changelog](../CHAN ### Adding a new KTX module -Adding a new library to KTX: -- Create folder matching module name in root of the repository. Modules should generally be named with a single word; -if using multiple words, use dash (`-`) as separator. -- Add folder name to `settings.gradle` file. This will also server as the project identifier that you use in -`build.gradle` scripts and to run individual Gradle tasks (like `gradle actors:test`). +- Create folder matching module name in root of the repository. Modules should generally be named with a single word. +When multiple words are necessary, use a single dash (`-`) as the word separator. +- Add folder name to `settings.gradle` file. This will also serve as the project identifier that you use in +`build.gradle` scripts and to run individual Gradle tasks (e.g. `gradle actors:test`). - Create `src/main/kotlin` and `src/test/kotlin` directories in your module folder. They will be automatically marked as source thanks to Gradle. You should also create package structure matching `ktx/your/module` in each source folder. - Add `gradle.properties` file with the following properties: @@ -102,20 +122,27 @@ projectName=ktx-your-module projectDesc=Description of your module as it will appear in Maven Central. ``` -- Add `build.gradle` file. It should contain dependencies specific to your module. If there are none, you can leave it +- Add a `build.gradle` file. It should contain dependencies specific to your module. If there are none, you can leave it empty. -- Add `README.md` file describing your module. Refer to other `README.md` files for guidelines. -- Add short description of the module to root `README.md`. +- Add a `README.md` file describing your module. Refer to other `README.md` files for guidelines. `README.md` files +should generally consist of the following sections: + - _General description_ - in a single sentence, what problem does the module solve? + - _Motivation_ - why was the module created? + - _Guide_ - what features does the module provide? Does it require additional setup? + - _Usage examples_ - how to use the module? + - _Synergy_ - is the module complemented by any other KTX libraries? + - _Alternatives_ - are there any other libraries or modules that can be used instead? + - _Additional documentation_ - are there any other guides or articles on the topic? - Your final module structure should roughly match this schema: ``` > your-module/ > src/ > main/kotlin/ktx/your/module/ - > yourModule.kt + - yourModule.kt > test/kotlin/ktx/your/module/ - > yourModuleTest.kt - > build.gradle - > gradle.properties - > README.md + - yourModuleTest.kt + - build.gradle + - gradle.properties + - README.md ``` diff --git a/.github/CONTRIBUTORS.md b/.github/CONTRIBUTORS.md index b2aa670d..5641e261 100644 --- a/.github/CONTRIBUTORS.md +++ b/.github/CONTRIBUTORS.md @@ -1,3 +1,5 @@ +# Contributors + Project contributors listed chronologically. * [@czyzby](https://github.com/czyzby) @@ -7,13 +9,13 @@ Project contributors listed chronologically. * [@MrPlow442](https://github.com/MrPlow442) * Contributed LibGDX [collections](../collections) utilities. * [@sreich](https://github.com/sreich) - * Contributed various utilities from [Ore Infinium](https://github.com/sreich/ore-infinium) project. + * Contributed various utilities from the [Ore Infinium](https://github.com/sreich/ore-infinium) project. * [@raincole](https://github.com/raincole) * Contributed LibGDX [collections](../collections) utilities. * Provided insightful review of the [`Async`](../async) module. * [@Jkly](https://github.com/Jkly) * Author of the [`Ashley` module](../ashley). - * Author of the [gdx-box2d-kotlin](https://github.com/Jkly/gdx-box2d-kotlin) library, which inspired the `Box2D` **KTX** module. + * Author of the [gdx-box2d-kotlin](https://github.com/Jkly/gdx-box2d-kotlin) library, which inspired the [`Box2D`](../box2d) **KTX** module. * Provided insightful review of the [`Box2D`](../box2d) module. Contributed ray casting and AABB query utilities. * [@keturn](https://github.com/keturn) * Suggested the [`FreeType` extension](../freetype). @@ -56,3 +58,7 @@ Project contributors listed chronologically. * Added utilities to [graphics module](../graphics). * [@rhutsaliuk](https://github.com/rhutsaliuk) * Contributed [json](../json) serializers utilities. + +### Metrics + +See [the GitHub insights](https://github.com/libktx/ktx/graphs/contributors). diff --git a/README.md b/README.md index d02493dc..85a3a48a 100644 --- a/README.md +++ b/README.md @@ -5,78 +5,130 @@ [![KTX](.github/ktx-logo.png "KTX")](http://libktx.github.io) -_**K**o**t**lin utilities for LibGD**X** applications._ +_**K**o**t**lin extensions for LibGD**X**._ -### About the project +## Introduction **KTX** aims to make [LibGDX](http://libgdx.badlogicgames.com/) as [Kotlin](http://kotlinlang.org/)-friendly as possible -without turning the API upside down. It provides modular utilities for certain parts of LibGDX with poor Kotlin support. -This is *not* a new framework by any means. +without completely rewriting the API. It provides modular utilities and extensions for selected parts of LibGDX with +poor native Kotlin support. -### Modules +Examples of Kotlin language features used to improve usability, performance and readability of LibGDX APIs include: -**KTX** was designed to be modular from day one: in fact, some of these libraries are just a single Kotlin file. After all, -you might not want or need all modules in your application. When possible, the extensions do not depend on the standard -Kotlin library - and even if they do, it is marked as a `provided` dependency. You can choose the Kotlin version that suits -you best and it will _not_ be overridden by your build system. +* *Operator overloads* for collections and mathematical operations. +* *Extension methods* with sensible *default parameters*. +* *Inline methods* with reduced runtime overhead for various listeners, builders and loggers. +* *Type-safe builders* for GUI and physics. +* *Coroutines context* providing easier concurrency. +* *Reified types* that simplify usage of methods normally consuming `Class` parameters. + +## Modules + +**KTX** was designed to be modular from day one - in fact, some of these libraries are just a single Kotlin file. +You can include selected **KTX** modules based on the needs of your application. Current **KTX** modules: -- [actors](actors): general `Scene2D` utilities for stages, actors, actions and event listeners. -- [app](app): `ApplicationListener` abstract implementations and other general LibGDX application utilities. -- [ashley](ashley): `Ashley` entity-component-system utilities. -- [assets](assets): resources management utilities. -- [async](async): [coroutines](https://kotlinlang.org/docs/reference/coroutines.html) context based on LibGDX threading model. -- [box2d](box2d): `Box2D` physics engine utilities. -- [collections](collections): extensions for LibGDX custom collections. Based on Kotlin standard library utilities. -- [freetype](freetype): FreeType font loading utilities. -- [graphics](graphics): utilities related to rendering tools and graphics. -- [i18n](i18n): some simple extensions that make LibGDX internationalization API less verbose, safer and easier to use. -- [inject](inject): unsettlingly simple dependency injection with nearly zero runtime overhead and no reflection trickery. -- [json](json): utilities for LibGDX JSON serialization class. -- [log](log): minimal runtime overhead cross-platform logging using inlined blocks. -- [math](math): operator overloads for LibGDX math API and general math utilities. -- [scene2d](scene2d): type-safe Kotlin builders for `Scene2D` GUI. -- [style](style): enhances `Skin` API with type-safe builders of official Scene2D widget styles. Replace your JSON skin file with type-safe DSL. -- [vis](vis): type-safe Kotlin builders for `VisUI`. An _alternative_ to the [scene2d](scene2d) module. -- [vis-style](vis-style): enhances `Skin` API with type-safe builders of `VisUI` widget styles. An _extension_ of [style](style) module. - -Note that most guides and examples assume that the reader is at least a bit familiar with the LibGDX API. - -Afraid to use some third-party code? Browse through the sources, run the test suites. While certainly not perfect, we try to -keep the public API clean and *every* feature fully unit tested. *Note: coverage tools still have problems with Kotlin -inlined methods, so the reported code coverage might be much lower than the actual.* - -### Dependencies - -**KTX** libraries are currently in late beta. While tested and stable enough, we want to give them a little bit -more time and get some user feedback before a stable release. All versions are available through Maven Central and -[here](https://github.com/libktx/ktx/releases). You can also use the preview snapshot releases from -`https://oss.sonatype.org/content/repositories/snapshots/` repository. - -All libraries follow the same naming schema - this is an example Gradle dependency: +Module | Dependency | Description +:---: | :--- | --- +[actors](actors) | `ktx-actors` | General `Scene2D` utilities for stages, actors, actions and event listeners. +[app](app) | `ktx-app` | `ApplicationListener` implementations and other general LibGDX application utilities. +[ashley](ashley) | `ktx-ashley` | `Ashley` entity-component-system utilities. +[assets](assets) | `ktx-assets` | Resources management utilities. +[async](async) | `ktx-async` | [Coroutines](https://kotlinlang.org/docs/reference/coroutines.html) context based on LibGDX threading model. +[box2d](box2d) | `ktx-box2d` | `Box2D` physics engine utilities. +[collections](collections) | `ktx-collections` | Extensions for LibGDX custom collections. +[freetype](freetype) | `ktx-freetype` | `FreeType` font loading utilities. +[graphics](graphics) | `ktx-graphics` | Utilities related to rendering tools and graphics. +[i18n](i18n) | `ktx-i18n` | Internationalization API utilities. +[inject](inject) | `ktx-inject` | A simple dependency injection system with nearly zero runtime overhead and no reflection usage. +[json](json) | `ktx-json` | Utilities for LibGDX JSON serialization API. +[log](log) | `ktx-log` | Minimal runtime overhead cross-platform logging using inlined functions. +[math](math) | `ktx-math` | Operator functions for LibGDX math API and general math utilities. +[scene2d](scene2d) | `ktx-scene2d` | Type-safe Kotlin builders for `Scene2D` GUI. +[style](style) | `ktx-style` | Type-safe Kotlin builders for `Scene2D` widget styles extending `Skin` API. +[vis](vis) | `ktx-vis` | Type-safe Kotlin builders for [`VisUI`](https://github.com/kotcrab/vis-ui/). An _alternative_ to the [scene2d](scene2d) module. +[vis-style](vis-style) | `ktx-vis-style` | Type-safe Kotlin builders for `VisUI` widget styles. An _extension_ of [style](style) module. + +### Installation + +**KTX** modules are uploaded to _Maven Central_ and are fully compatible with the Gradle build tool used by LibGDX +by default. + +All libraries follow the same naming schema: ```Groovy -compile "io.github.libktx:ktx-$module:$ktxVersion" +compile "io.github.libktx:$module:$ktxVersion" ``` -Replace `$module` with the name of required **KTX** library. `$ktxVersion` usually matches LibGDX version it was -compiled against - although it might end with `-b1` (if it is a beta release) or `-SNAPSHOT` (if you are using -the snapshots). For example, the first official beta release with the current group ID was compiled against LibGDX -`1.9.6` and its version was `1.9.6-b2`. You can browse through our releases -[here](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22). +Replace `$module` with the name of the selected **KTX** library. + +**KTX** versions match the LibGDX versions that they were compiled against. `$ktxVersion` will usually match your LibGDX +version, but it might end with `-b` postfix if it is a beta release or `-SNAPSHOT` if you are using the development branch. + +For example, the first official beta release with the current group ID `io.github.libktx` was compiled against +LibGDX `1.9.6` and its version was `1.9.6-b2`. The corresponding snapshot release of this version was `1.9.6-SNAPSHOT`. + +You can browse through our official releases [on Maven](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22) +or [on GitHub](https://github.com/libktx/ktx/releases). + +Although **KTX** is still in late beta, the official releases are stable enough for production use. +All modules are thoroughly tested with unit tests. -Note that even snapshots should be more or less stable, as libraries are not pushed to _Maven Central_ unless they pass -the extensive tests. +#### Latest changes -### Documentation +The [`master`](https://github.com/libktx/ktx/tree/master/) branch is the default branch of the repository. However, +it represents the latest stable release of **KTX**. The latest changes can be found on the +[`develop`](https://github.com/libktx/ktx/tree/develop/) branch. + +Note that you can also use the preview snapshot releases with the latest changed from the +`https://oss.sonatype.org/content/repositories/snapshots/` repository. The latest snapshot version can be found on +the [`develop`](https://github.com/libktx/ktx/blob/develop/version.txt) branch. Even the snapshots should be more or +less stable, as the libraries are not pushed to _Maven Central_ unless they pass their extensive test suites. + +## Documentation + +### Official guides Each module contains a `README.md` file with a list of all features or a guide with a few code snippets. Browse through -the directories in root folder to find out more about each library. GitHub releases also contain archives with generated -Dokka documentation for each module. [KTX wiki](https://github.com/libktx/ktx/wiki) lists some useful resources as well. +the directories in root folder to find out more about each library. + +### Source documentation + +All public classes and functions are also documented with standard Kotlin _KDocs_. GitHub releases contain archives +with generated Dokka documentation for each module, although you can go through the documentation by viewing the sources +directly. + +### Links + +[KTX wiki](https://github.com/libktx/ktx/wiki) lists some useful resources that can help you get started. + +Note that most official guides and examples in this repository assume that the reader is at least a bit familiar with +the LibGDX API. If you are just getting to know the framework, it might be helpful to go through +[the official LibGDX wiki](https://github.com/libgdx/libgdx/wiki). + +### `android-ktx` + +Note that [`android-ktx`](https://github.com/android/android-ktx) is a separate project with official Android utilities. +The "**KTX**" name was chosen long before the Android project was announced. + +## [Contribution](.github/CONTRIBUTING.md) + +Suggestions, questions, typo fixes, documentation improvements and code contributions are always welcome. +If you would like to contribute, please read [the contribution guideline](.github/CONTRIBUTING.md) and browse through +[the active issues](https://github.com/libktx/ktx/issues). Don't be afraid to create issues just to ask a question or +make a request for any kind of improvement. + +The [`develop`](https://github.com/libktx/ktx/tree/develop/) is the active development branch. When creating pull +requests, make sure to choose `develop` as the target branch. + +You can see the list of the contributors [via GitHub insights](https://github.com/libktx/ktx/graphs/contributors) +and on [the contributors list](.github/CONTRIBUTORS.md). + +### Licensing + +Before creating any pull requests, be aware that the code is dedicated to [public domain](LICENSE.txt). -### [Contribution and working from sources](.github/CONTRIBUTING.md) +### Working from sources -If you want to help, read the [contribution](.github/CONTRIBUTING.md) guideline and browse through the issues to see -what's currently to do. Don't be afraid to create issues just to ask a question or make a request for any kind of -improvements. Before creating any pull requests, be aware that the code is dedicated to [public domain](LICENSE.txt). +See [this section](.github/CONTRIBUTING.md#working-from-sources) of the contribution guideline to get started. From 22689c9d6a639141dc9d10360c0b8477ecfab42d Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 02:39:11 +0100 Subject: [PATCH 32/45] Added Gradle setup examples. #231 --- README.md | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 85a3a48a..3c147b12 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Module | Dependency | Description [freetype](freetype) | `ktx-freetype` | `FreeType` font loading utilities. [graphics](graphics) | `ktx-graphics` | Utilities related to rendering tools and graphics. [i18n](i18n) | `ktx-i18n` | Internationalization API utilities. -[inject](inject) | `ktx-inject` | A simple dependency injection system with nearly zero runtime overhead and no reflection usage. +[inject](inject) | `ktx-inject` | A simple dependency injection system with low overhead and no reflection usage. [json](json) | `ktx-json` | Utilities for LibGDX JSON serialization API. [log](log) | `ktx-log` | Minimal runtime overhead cross-platform logging using inlined functions. [math](math) | `ktx-math` | Operator functions for LibGDX math API and general math utilities. @@ -68,6 +68,20 @@ version, but it might end with `-b` postfix if it is a beta release or `-SNAPSHO For example, the first official beta release with the current group ID `io.github.libktx` was compiled against LibGDX `1.9.6` and its version was `1.9.6-b2`. The corresponding snapshot release of this version was `1.9.6-SNAPSHOT`. +Including `ktx-app` module would require the following changes in your `build.gradle` file: + +```Groovy +ext { + // Update this version to match the latest release: + ktxVersion = '1.9.6-b2' +} + +dependencies { + compile "io.github.libktx:ktx-app:$ktxVersion" +} +``` + +**KTX** modules should generally be added to the shared `core` module of your LibGDX application. You can browse through our official releases [on Maven](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22) or [on GitHub](https://github.com/libktx/ktx/releases). @@ -82,9 +96,18 @@ it represents the latest stable release of **KTX**. The latest changes can be fo [`develop`](https://github.com/libktx/ktx/tree/develop/) branch. Note that you can also use the preview snapshot releases with the latest changed from the -`https://oss.sonatype.org/content/repositories/snapshots/` repository. The latest snapshot version can be found on -the [`develop`](https://github.com/libktx/ktx/blob/develop/version.txt) branch. Even the snapshots should be more or -less stable, as the libraries are not pushed to _Maven Central_ unless they pass their extensive test suites. +`https://oss.sonatype.org/content/repositories/snapshots/` repository. + +```Groovy +repositories { + // Include your default repositories here. + maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } +} +``` + +The latest snapshot version can be found on the [`develop`](https://github.com/libktx/ktx/blob/develop/version.txt) +branch. Even the snapshots should be more or less stable, as the libraries are not pushed to _Maven Central_ unless +they pass their extensive test suites. ## Documentation From d6021f4ed210e7537a608caee06d03b4364dc113 Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 02:53:37 +0100 Subject: [PATCH 33/45] Expanded versioning documentation. #231 --- README.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3c147b12..33fc9b93 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,10 @@ Examples of Kotlin language features used to improve usability, performance and * *Operator overloads* for collections and mathematical operations. * *Extension methods* with sensible *default parameters*. * *Inline methods* with reduced runtime overhead for various listeners, builders and loggers. -* *Type-safe builders* for GUI and physics. -* *Coroutines context* providing easier concurrency. +* *Nullable types* which improve typing information of selected interfaces and functions. +* *Default interface methods* simplifying the implementation. +* *Type-safe builders* for GUI, styling and physics engine. +* *Coroutines context* providing concurrency utilities. * *Reified types* that simplify usage of methods normally consuming `Class` parameters. ## Modules @@ -31,7 +33,7 @@ Current **KTX** modules: Module | Dependency | Description :---: | :--- | --- -[actors](actors) | `ktx-actors` | General `Scene2D` utilities for stages, actors, actions and event listeners. +[actors](actors) | `ktx-actors` | General `Scene2D` GUI utilities for stages, actors, actions and event listeners. [app](app) | `ktx-app` | `ApplicationListener` implementations and other general LibGDX application utilities. [ashley](ashley) | `ktx-ashley` | `Ashley` entity-component-system utilities. [assets](assets) | `ktx-assets` | Resources management utilities. @@ -63,11 +65,6 @@ compile "io.github.libktx:$module:$ktxVersion" Replace `$module` with the name of the selected **KTX** library. -**KTX** versions match the LibGDX versions that they were compiled against. `$ktxVersion` will usually match your LibGDX -version, but it might end with `-b` postfix if it is a beta release or `-SNAPSHOT` if you are using the development branch. - -For example, the first official beta release with the current group ID `io.github.libktx` was compiled against -LibGDX `1.9.6` and its version was `1.9.6-b2`. The corresponding snapshot release of this version was `1.9.6-SNAPSHOT`. Including `ktx-app` module would require the following changes in your `build.gradle` file: ```Groovy @@ -81,11 +78,23 @@ dependencies { } ``` -**KTX** modules should generally be added to the shared `core` module of your LibGDX application. +**KTX** modules should generally be added to the dependencies of the shared `core` module of your LibGDX application. + +#### Versioning + +**KTX** versions match the LibGDX versions that they were compiled against. `$ktxVersion` will usually match your LibGDX +version, but it might end with `-b` postfix if it is a beta release or `-SNAPSHOT` if you are using the development branch. + +For example, the first official beta release with the current group ID `io.github.libktx` was compiled against +LibGDX `1.9.6` and since it was the second beta release, its version was `1.9.6-b2`. The corresponding snapshot release +of this version was `1.9.6-SNAPSHOT`. You can browse through our official releases [on Maven](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22) or [on GitHub](https://github.com/libktx/ktx/releases). +Unfortunately, LibGDX does not follow the [semantic versioning](https://semver.org/) guidelines. Both minor and patch +versions can introduce breaking changes. Please read the LibGDX and KTX change logs before updating. + Although **KTX** is still in late beta, the official releases are stable enough for production use. All modules are thoroughly tested with unit tests. From 04e791a302f8c58e48e58c4a040bbb7fbed2484b Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 03:01:20 +0100 Subject: [PATCH 34/45] Added Ashley to major dependencies. #23 --- .github/CONTRIBUTING.md | 2 ++ ashley/README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 90ff61b1..b4ed77d6 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -104,6 +104,8 @@ all of the dependencies (outside of testing scope) up-to-date. Major dependencie `distributionUrl` rather than just the binaries (`bin`). - **VisUI**: update `visUiVersion` in the properties file and VisUI version in the tag on the top of the [vis/README.md](../vis/README.md) file. +- **Ashley**: update `ashleyVersion` in the properties file and Ashely version in the tag on the top of the +[ashley/README.md](../ashley/README.md) file. All of the major dependencies updates should be added to the [changelog](../CHANGELOG.md). diff --git a/ashley/README.md b/ashley/README.md index 7ad4f41d..0aa0c40c 100644 --- a/ashley/README.md +++ b/ashley/README.md @@ -1,3 +1,5 @@ +[![Ashley](https://img.shields.io/badge/ashley-1.7.3-red.svg)](https://libgdx.badlogicgames.com/) + # KTX: `Ashley` entity component system utilities Utilities and type-safe builders for the [Ashley](https://github.com/libgdx/ashley) entity component system. From 4d358c2e2a445736357df2ca6e7770220d5ef53a Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 03:07:34 +0100 Subject: [PATCH 35/45] Improved ktx-app documentation. #231 --- app/README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/README.md b/app/README.md index 9cca78c0..0628e679 100644 --- a/app/README.md +++ b/app/README.md @@ -1,11 +1,11 @@ # KTX: basic application utilities -Abstract `ApplicationListener` implementations and general LibGDX utilities. +Basic `ApplicationListener` implementations and general LibGDX utilities. ### Why? LibGDX offers some basic `ApplicationListener` implementations in form of `ApplicationAdapter` and `Game`, but both are -pretty basic. They do not handle screen clearing or fixed rendering time step, both of which often have to be set up +pretty bare-bones. They do not handle screen clearing or manage views list, both of which often have to be set up manually in LibGDX applications. This module aims to provide a simple base for your custom `ApplicationListener`: if you do not have your favorite setup implemented just yet, it might be a good idea to base it on abstract classes provided by `ktx-app`. @@ -14,16 +14,17 @@ by `ktx-app`. #### `ApplicationListener` implementations -- `KtxApplicationAdapter` is an `ApplicationListener` extension. Provides no-op implementations of all methods, without -being an abstract class like `com.badlogic.gdx.ApplicationAdapter`. +- `KtxApplicationAdapter` is an interface that extends `ApplicationListener`. Provides no-op implementations of all +methods, without being an abstract class like `com.badlogic.gdx.ApplicationAdapter`, which makes it more flexible. - `KtxGame` is a bit more opinionated `Game` equivalent that not only delegates all game events to the current `Screen` instance, but also ensures non-nullability of screens, manages screen clearing, and maintains screens collection, which -allows switching screens while knowing only their concrete class. `KtxScreen` is an interface extending `Screen` that -provides no-op method implementations, making all methods optional to override. +allows switching screens while knowing only their concrete class. +*`KtxScreen` is an interface extending `Screen` that provides no-op method implementations, making all methods optional +to override. #### `InputProcessor` implementations -- `KtxInputAdapter` is an `InputProcessor` extension. Provides no-op implementations of all methods, without +- `KtxInputAdapter` is an interface extending `InputProcessor`. Provides no-op implementations of all methods, without being an abstract class like `com.badlogic.gdx.InputAdapter`. #### Miscellaneous utilities From db726f027a18aac3b79c5502dc49ec77edc95694 Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 03:15:31 +0100 Subject: [PATCH 36/45] Improved ktx-freetype documentation. #231 --- freetype/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/freetype/README.md b/freetype/README.md index 6c668562..460bf45c 100644 --- a/freetype/README.md +++ b/freetype/README.md @@ -9,7 +9,7 @@ aims to limit the boilerplate necessary to load FreeType fonts in LibGDX applica ### Guide -This module consists of the following functions: +This module consists of the following utilities: * Extension method `AssetManager.registerFreeTypeFontLoaders` allows to register all loaders required to load FreeType font assets. It should be called right after constructing a `AssetManager` instance and before loading any assets. @@ -29,7 +29,7 @@ import ktx.freetype.* fun initiateAssetManager(): AssetManager { val assetManager = AssetManager() - // Calling registerFreeTypeFontLoaders is necessary in order to load TTF/OTF files. + // Calling registerFreeTypeFontLoaders is necessary in order to load TTF/OTF files: assetManager.registerFreeTypeFontLoaders() return assetManager } @@ -83,6 +83,7 @@ val font = assetManager.get("font.ttf") Using delegation to schedule loading of a FreeType font: ```kotlin +import com.badlogic.gdx.graphics.g2d.BitmapFont import ktx.assets.getValue import ktx.freetype.loadFreeTypeFont @@ -129,6 +130,9 @@ val fontB = generator.generateFont { } ``` +### Synergy + +This module uses [`ktx-assets`](../assets) internally to improve `AssetManager` API. ### Alternatives From c2a200bc64027d2264400eff5113f4da487eb582 Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 03:17:24 +0100 Subject: [PATCH 37/45] Removed ktx-assets-async reference. #182 --- assets/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/README.md b/assets/README.md index 9b586504..fcd67fa2 100644 --- a/assets/README.md +++ b/assets/README.md @@ -370,8 +370,6 @@ injects assets into annotated fields thanks to reflection. - [Kiwi](https://github.com/czyzby/gdx-lml/tree/master/kiwi) library has some utilities for assets handling, like graceful `Disposable` destruction methods and LibGDX collections implementing `Disposable` interface. It is aimed at Java applications though - **KTX** syntax should feel more natural when using Kotlin. -- [`ktx-assets-async`](../assets-async) module extends this library and provides `AssetStorage`: a lightweight -coroutines-based alternative to `AssetManager`. #### Additional documentation From a0b58a9b79fea5dbf2f481492652a66b64dec7e3 Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 05:33:21 +0100 Subject: [PATCH 38/45] Added link to the Choosing KTX article. #231 --- README.md | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 33fc9b93..d5315d46 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Examples of Kotlin language features used to improve usability, performance and * *Coroutines context* providing concurrency utilities. * *Reified types* that simplify usage of methods normally consuming `Class` parameters. +See the [_Choosing KTX_](https://github.com/libktx/ktx/wiki/Choosing-KTX) article for pros and cons of this framework. + ## Modules **KTX** was designed to be modular from day one - in fact, some of these libraries are just a single Kotlin file. @@ -31,23 +33,23 @@ You can include selected **KTX** modules based on the needs of your application. Current **KTX** modules: -Module | Dependency | Description +Module | Dependency name | Description :---: | :--- | --- -[actors](actors) | `ktx-actors` | General `Scene2D` GUI utilities for stages, actors, actions and event listeners. -[app](app) | `ktx-app` | `ApplicationListener` implementations and other general LibGDX application utilities. -[ashley](ashley) | `ktx-ashley` | `Ashley` entity-component-system utilities. +[actors](actors) | `ktx-actors` | General [`Scene2D`](https://github.com/libgdx/libgdx/wiki/Scene2d) GUI utilities for stages, actors, actions and event listeners. +[app](app) | `ktx-app` | `ApplicationListener` implementations and other general application utilities. +[ashley](ashley) | `ktx-ashley` | [`Ashley`](https://github.com/libgdx/ashley) entity-component-system utilities. [assets](assets) | `ktx-assets` | Resources management utilities. [async](async) | `ktx-async` | [Coroutines](https://kotlinlang.org/docs/reference/coroutines.html) context based on LibGDX threading model. -[box2d](box2d) | `ktx-box2d` | `Box2D` physics engine utilities. +[box2d](box2d) | `ktx-box2d` | [`Box2D`](https://github.com/libgdx/libgdx/wiki/Box2d) physics engine utilities. [collections](collections) | `ktx-collections` | Extensions for LibGDX custom collections. [freetype](freetype) | `ktx-freetype` | `FreeType` font loading utilities. [graphics](graphics) | `ktx-graphics` | Utilities related to rendering tools and graphics. [i18n](i18n) | `ktx-i18n` | Internationalization API utilities. [inject](inject) | `ktx-inject` | A simple dependency injection system with low overhead and no reflection usage. -[json](json) | `ktx-json` | Utilities for LibGDX JSON serialization API. +[json](json) | `ktx-json` | Utilities for LibGDX [JSON](https://github.com/libgdx/libgdx/wiki/Reading-and-writing-JSON) serialization API. [log](log) | `ktx-log` | Minimal runtime overhead cross-platform logging using inlined functions. [math](math) | `ktx-math` | Operator functions for LibGDX math API and general math utilities. -[scene2d](scene2d) | `ktx-scene2d` | Type-safe Kotlin builders for `Scene2D` GUI. +[scene2d](scene2d) | `ktx-scene2d` | Type-safe Kotlin builders for [`Scene2D`](https://github.com/libgdx/libgdx/wiki/Scene2d) GUI. [style](style) | `ktx-style` | Type-safe Kotlin builders for `Scene2D` widget styles extending `Skin` API. [vis](vis) | `ktx-vis` | Type-safe Kotlin builders for [`VisUI`](https://github.com/kotcrab/vis-ui/). An _alternative_ to the [scene2d](scene2d) module. [vis-style](vis-style) | `ktx-vis-style` | Type-safe Kotlin builders for `VisUI` widget styles. An _extension_ of [style](style) module. @@ -65,11 +67,11 @@ compile "io.github.libktx:$module:$ktxVersion" Replace `$module` with the name of the selected **KTX** library. -Including `ktx-app` module would require the following changes in your `build.gradle` file: +For example, including `ktx-app` module would require the following changes in your `build.gradle` file: ```Groovy ext { - // Update this version to match the latest release: + // Update this version to match the latest KTX release: ktxVersion = '1.9.6-b2' } @@ -78,6 +80,9 @@ dependencies { } ``` +Note that defining `ktxVersion` is not necessary and versions can be set directly in the `dependencies` section, +but extracting the versions is a good practice and will speed up updating if you include multiple KTX modules. + **KTX** modules should generally be added to the dependencies of the shared `core` module of your LibGDX application. #### Versioning @@ -93,7 +98,7 @@ You can browse through our official releases [on Maven](https://search.maven.org or [on GitHub](https://github.com/libktx/ktx/releases). Unfortunately, LibGDX does not follow the [semantic versioning](https://semver.org/) guidelines. Both minor and patch -versions can introduce breaking changes. Please read the LibGDX and KTX change logs before updating. +versions can introduce breaking changes. Please read the LibGDX and [**KTX** change logs](CHANGELOG.md) before updating. Although **KTX** is still in late beta, the official releases are stable enough for production use. All modules are thoroughly tested with unit tests. @@ -101,20 +106,27 @@ All modules are thoroughly tested with unit tests. #### Latest changes The [`master`](https://github.com/libktx/ktx/tree/master/) branch is the default branch of the repository. However, -it represents the latest stable release of **KTX**. The latest changes can be found on the +it represents the last stable release of **KTX**. The latest changes can be found on the [`develop`](https://github.com/libktx/ktx/tree/develop/) branch. -Note that you can also use the preview snapshot releases with the latest changed from the -`https://oss.sonatype.org/content/repositories/snapshots/` repository. +You do not have to compile the sources manually to use the latest features. The preview snapshot releases are uploaded +to the `https://oss.sonatype.org/content/repositories/snapshots/` repository. To use them in your application, add +the following Maven repository and modify the prefix of `ktxVersion` to `-SNAPSHOT`: ```Groovy repositories { // Include your default repositories here. maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } } + +ext { + // Update this version to match the latest LibGDX release: + ktxVersion = '1.9.6-SNAPSHOT' +} + ``` -The latest snapshot version can be found on the [`develop`](https://github.com/libktx/ktx/blob/develop/version.txt) +The latest snapshot version name can be found on the [`develop`](https://github.com/libktx/ktx/blob/develop/version.txt) branch. Even the snapshots should be more or less stable, as the libraries are not pushed to _Maven Central_ unless they pass their extensive test suites. @@ -122,7 +134,7 @@ they pass their extensive test suites. ### Official guides -Each module contains a `README.md` file with a list of all features or a guide with a few code snippets. Browse through +Each module contains a `README.md` file with a list of all features or a guide with some code snippets. Browse through the directories in root folder to find out more about each library. ### Source documentation @@ -148,13 +160,13 @@ The "**KTX**" name was chosen long before the Android project was announced. Suggestions, questions, typo fixes, documentation improvements and code contributions are always welcome. If you would like to contribute, please read [the contribution guideline](.github/CONTRIBUTING.md) and browse through -[the active issues](https://github.com/libktx/ktx/issues). Don't be afraid to create issues just to ask a question or +[the active issues](https://github.com/libktx/ktx/issues). Don't hesitate to create issues just to ask a question or make a request for any kind of improvement. The [`develop`](https://github.com/libktx/ktx/tree/develop/) is the active development branch. When creating pull requests, make sure to choose `develop` as the target branch. -You can see the list of the contributors [via GitHub insights](https://github.com/libktx/ktx/graphs/contributors) +You can check the list of the contributors via [GitHub insights](https://github.com/libktx/ktx/graphs/contributors) and on [the contributors list](.github/CONTRIBUTORS.md). ### Licensing From 674ad4bea76a42e6732afc02135915ce5a10fcba Mon Sep 17 00:00:00 2001 From: MJ Date: Wed, 18 Dec 2019 11:24:33 +0100 Subject: [PATCH 39/45] Improved Gradle configuration examples. #231 --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d5315d46..5e4e6ea9 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,6 @@ See the [_Choosing KTX_](https://github.com/libktx/ktx/wiki/Choosing-KTX) articl **KTX** was designed to be modular from day one - in fact, some of these libraries are just a single Kotlin file. You can include selected **KTX** modules based on the needs of your application. -Current **KTX** modules: - Module | Dependency name | Description :---: | :--- | --- [actors](actors) | `ktx-actors` | General [`Scene2D`](https://github.com/libgdx/libgdx/wiki/Scene2d) GUI utilities for stages, actors, actions and event listeners. @@ -67,7 +65,8 @@ compile "io.github.libktx:$module:$ktxVersion" Replace `$module` with the name of the selected **KTX** library. -For example, including `ktx-app` module would require the following changes in your `build.gradle` file: +For example, including the [app](app) module with the `ktx-app` identifier would require the following changes +in your `build.gradle` file: ```Groovy ext { @@ -80,8 +79,9 @@ dependencies { } ``` -Note that defining `ktxVersion` is not necessary and versions can be set directly in the `dependencies` section, -but extracting the versions is a good practice and will speed up updating if you include multiple KTX modules. +Note that defining `ktxVersion` is not necessary, as versions can be defined directly in the `dependencies` section. +However, extracting the dependencies versions is a good practice, especially if they can be reused throughout the +build files. This will speed up updating of your project if you include multiple KTX modules. **KTX** modules should generally be added to the dependencies of the shared `core` module of your LibGDX application. @@ -95,7 +95,7 @@ LibGDX `1.9.6` and since it was the second beta release, its version was `1.9.6- of this version was `1.9.6-SNAPSHOT`. You can browse through our official releases [on Maven](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22) -or [on GitHub](https://github.com/libktx/ktx/releases). +and [on GitHub](https://github.com/libktx/ktx/releases). Unfortunately, LibGDX does not follow the [semantic versioning](https://semver.org/) guidelines. Both minor and patch versions can introduce breaking changes. Please read the LibGDX and [**KTX** change logs](CHANGELOG.md) before updating. @@ -127,15 +127,17 @@ ext { ``` The latest snapshot version name can be found on the [`develop`](https://github.com/libktx/ktx/blob/develop/version.txt) -branch. Even the snapshots should be more or less stable, as the libraries are not pushed to _Maven Central_ unless -they pass their extensive test suites. +branch. + +Even the snapshots should be more or less stable, as the libraries are not pushed to _Maven Central_ unless they pass +their extensive test suites. ## Documentation ### Official guides Each module contains a `README.md` file with a list of all features or a guide with some code snippets. Browse through -the directories in root folder to find out more about each library. +the directories in the root folder to find out more about each library. ### Source documentation From 80a54fc7aaf689a20750d97dddb0e26408f7d3c3 Mon Sep 17 00:00:00 2001 From: dakeese Date: Thu, 19 Dec 2019 22:05:06 -0500 Subject: [PATCH 40/45] Optional pool freeing when using removeAll or retainAll --- collections/README.md | 3 ++- .../src/main/kotlin/ktx/collections/arrays.kt | 12 ++++++++-- .../test/kotlin/ktx/collections/arraysTest.kt | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/collections/README.md b/collections/README.md index 0573988c..a8321190 100644 --- a/collections/README.md +++ b/collections/README.md @@ -32,7 +32,8 @@ chained. - Missing `addAll` and `removeAll` methods for arrays and iterables were added. - `iterate` method allows to iterate over collection's elements, while providing reference to `MutableInterator`. Can be used to easily remove collection elements during iteration. -- `removeAll` and `retainAll` higher-order functions that work like functions in Kotlin stdlib +- `removeAll` and `retainAll` higher-order functions that work like functions in Kotlin stdlib. A Pool can optionally be +passed to automatically free the removed items. - `map`, `filter`, `flatten` and `flatMap` methods that work like methods in Kotlin stdlib but return `GdxArray`. - Every iterable and array can be converted to `Array` using `toGdxArray` method. - `IntArray`, `BooleanArray` and `FloatArray` can be converted to corresponding LibGDX primitive collections using diff --git a/collections/src/main/kotlin/ktx/collections/arrays.kt b/collections/src/main/kotlin/ktx/collections/arrays.kt index 40c50dad..d3f1d418 100644 --- a/collections/src/main/kotlin/ktx/collections/arrays.kt +++ b/collections/src/main/kotlin/ktx/collections/arrays.kt @@ -2,6 +2,8 @@ package ktx.collections +import com.badlogic.gdx.utils.Pool + /** Alias for [com.badlogic.gdx.utils.Array] avoiding name collision with the standard library. */ typealias GdxArray = com.badlogic.gdx.utils.Array /** Alias for [com.badlogic.gdx.utils.BooleanArray] avoiding name collision with the standard library. */ @@ -227,8 +229,9 @@ inline fun > GdxArray.sortByDescending(crossin /** * Removes elements from the array that satisfy the predicate. + * @param pool Removed items are freed to this pool. */ -inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { +inline fun GdxArray.removeAll(pool: Pool?, predicate: (Type) -> Boolean) { var currentWriteIndex = 0 for (i in 0 until size) { val value = items[i] @@ -237,6 +240,8 @@ inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { items[currentWriteIndex] = value } currentWriteIndex++ + } else { + pool?.free(value) } } truncate(currentWriteIndex) @@ -244,8 +249,9 @@ inline fun GdxArray.removeAll(predicate: (Type) -> Boolean) { /** * Removes elements from the array that do not satisfy the predicate. + * @param pool Removed items are freed to this optional pool. */ -inline fun GdxArray.retainAll(predicate: (Type) -> Boolean) { +inline fun GdxArray.retainAll(pool: Pool? = null, predicate: (Type) -> Boolean) { var currentWriteIndex = 0 for (i in 0 until size) { val value = items[i] @@ -254,6 +260,8 @@ inline fun GdxArray.retainAll(predicate: (Type) -> Boolean) { items[currentWriteIndex] = value } currentWriteIndex++ + } else { + pool?.free(value) } } truncate(currentWriteIndex) diff --git a/collections/src/test/kotlin/ktx/collections/arraysTest.kt b/collections/src/test/kotlin/ktx/collections/arraysTest.kt index 08d58383..acadf3ae 100644 --- a/collections/src/test/kotlin/ktx/collections/arraysTest.kt +++ b/collections/src/test/kotlin/ktx/collections/arraysTest.kt @@ -1,5 +1,8 @@ package ktx.collections +import com.badlogic.gdx.math.Vector2 +import com.badlogic.gdx.utils.Pool +import com.badlogic.gdx.utils.Pools import org.junit.Assert.* import org.junit.Test import java.util.LinkedList @@ -380,6 +383,16 @@ class ArraysTest { assertEquals(GdxArray(), array) } + @Test + fun `should free removed elements`() { + val array = GdxArray.with(Vector2(), Vector2(1f, 1f), Vector2(2f, 2f)) + val pool = object : Pool() { + override fun newObject() = Vector2() + } + array.removeAll(pool) { it.len() > 0.5f } + assertEquals(pool.peak, 2) + } + @Test fun `should retain elements from existing GdxArray`() { val array = GdxArray.with(1, 2, 3, 4, 5) @@ -396,6 +409,16 @@ class ArraysTest { assertEquals(GdxArray(), array) } + @Test + fun `should free unretained elements`() { + val array = GdxArray.with(Vector2(), Vector2(1f, 1f), Vector2(2f, 2f)) + val pool = object : Pool() { + override fun newObject() = Vector2() + } + array.retainAll(pool) { it.len() < 0.5f } + assertEquals(pool.peak, 2) + } + @Test fun `should map elements into a new GdxArray`() { val array = GdxArray.with(1, 2, 3) From 5e3a760cac1e7174e4e6b528afcd8a39dc3a82a3 Mon Sep 17 00:00:00 2001 From: dakeese Date: Thu, 19 Dec 2019 22:14:48 -0500 Subject: [PATCH 41/45] Fix formatting --- collections/src/main/kotlin/ktx/collections/arrays.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/collections/src/main/kotlin/ktx/collections/arrays.kt b/collections/src/main/kotlin/ktx/collections/arrays.kt index 80641409..2e4d5eae 100644 --- a/collections/src/main/kotlin/ktx/collections/arrays.kt +++ b/collections/src/main/kotlin/ktx/collections/arrays.kt @@ -6,16 +6,22 @@ import com.badlogic.gdx.utils.Pool /** Alias for [com.badlogic.gdx.utils.Array] avoiding name collision with the standard library. */ typealias GdxArray = com.badlogic.gdx.utils.Array + /** Alias for [com.badlogic.gdx.utils.BooleanArray] avoiding name collision with the standard library. */ typealias GdxBooleanArray = com.badlogic.gdx.utils.BooleanArray + /** Alias for [com.badlogic.gdx.utils.FloatArray] avoiding name collision with the standard library. */ typealias GdxFloatArray = com.badlogic.gdx.utils.FloatArray + /** Alias for [com.badlogic.gdx.utils.IntArray] avoiding name collision with the standard library. */ typealias GdxIntArray = com.badlogic.gdx.utils.IntArray + /** Alias for [com.badlogic.gdx.utils.CharArray] avoiding name collision with the standard library. */ typealias GdxCharArray = com.badlogic.gdx.utils.CharArray + /** Alias for [com.badlogic.gdx.utils.LongArray] avoiding name collision with the standard library. */ typealias GdxLongArray = com.badlogic.gdx.utils.LongArray + /** Alias for [com.badlogic.gdx.utils.ShortArray] avoiding name collision with the standard library. */ typealias GdxShortArray = com.badlogic.gdx.utils.ShortArray From dbf546f83f57a23da8a4188211d4e923c238bc3e Mon Sep 17 00:00:00 2001 From: MJ Date: Sun, 22 Dec 2019 12:10:58 +0100 Subject: [PATCH 42/45] Removed mentions of the global I18NBundle instance. #42 --- i18n/README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/i18n/README.md b/i18n/README.md index 3d7a21a8..7b04bb7d 100644 --- a/i18n/README.md +++ b/i18n/README.md @@ -1,12 +1,12 @@ # KTX: internationalization utilities -This tiny module is a thin wrapper over LibGDX `I18NBundle` with some "global" functions that ease the use of its API. +`I18NBundle` utilities. ### Why? As useful as `I18NBundle` is, it is often overlooked in pure Java applications due to sheer amount of work that it -requires when compared to the "lazy" approach of plain strings usage. Passing `I18NBundle` instance around is tedious, -and in the end i18n reloading might be so complex to implement, that it requires a complete application restart. +requires when compared to the "lazy" approach of plain strings usage. This module provides a thin layer of syntax sugar +that makes it easier to work with `I18NBundle` instances and provide some useful code completion. ### Guide @@ -37,7 +37,16 @@ enum class Nls : BundleLine { } ``` -Listing - or generating - all expected bundle lines (is basically all you have to do to enjoy less verbose and safer +This allows you to access bundle lines via enum instances: + +```Kotlin +import ktx.i18n.get +import ktx.i18n.example.Nls + +bundle[Nls.key1] +``` + +Listing - or generating - all expected bundle lines is basically all you have to do to enjoy less verbose and safer syntax with code completion and compile-time validation. See usage examples below to explore `BundleLine` API. Note that `BundleLine` assumes that `toString()` implementation returns a valid bundle line ID. If you want to change From 3349d3ebff394860849cd29ecfa1feaf925dd43a Mon Sep 17 00:00:00 2001 From: MJ Date: Sun, 22 Dec 2019 12:35:55 +0100 Subject: [PATCH 43/45] Added detailed descriptions of new modules. #17 --- CHANGELOG.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c500c94..c901a59b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,7 +141,7 @@ by the Ashley engine. This is still the case, but now an exception with a meanin - `rotate` - `scale` - `translate` - - `triange` + - `triangle` - **[CHANGE]** (`ktx-app`, `ktx-graphics`) Utility functions moved from `ktx-app` to the new `ktx-graphics`: - `color` - `Color.copy` @@ -267,11 +267,11 @@ documentation for migration guide. #### 1.9.6-b4 -- **[FEATURE]** (`ktx-collections`) Added `map`, `filter` and `flatten` extension methods that return LibGDX collections. -- **[FEATURE]** (`ktx-collections`) `PooledList` now properly implements `hashCode` and `equals`. - **[FEATURE]** (`ktx-app`) Added `KtxGame`: **KTX** equivalent of LibGDX `Game`. - **[FEATURE]** (`ktx-app`) Added `KtxScreen`: adapter of the LibGDX `Screen` interface making all methods optional to implement. - **[FEATURE]** (`ktx-app`) Added `emptyScreen` utility method returning a no-op implementation of `Screen`. +- **[FEATURE]** (`ktx-collections`) Added `map`, `filter` and `flatten` extension methods that return LibGDX collections. +- **[FEATURE]** (`ktx-collections`) `PooledList` now properly implements `hashCode` and `equals`. - **[FEATURE]** (`ktx-inject`) `Context` now implements `Disposable` and allows to dispose of all registered singletons and providers. - **[FEATURE]** (`ktx-inject`) Added `Context.remove` and `removeProvider` methods. Now providers for particular types can be removed without clearing the whole context. - **[FEATURE]** (`ktx-inject`) `getProvider`, `setProvider` and `clear` methods of `Context` are now open and can be overridden. @@ -404,13 +404,65 @@ collections. - **[UPDATE]** Updated to LibGDX 1.9.4. - **[FEATURE]** (`ktx-actors`) Implemented `ktx-actors` module. + - `isShown`, `centerPosition`, `setKeyBoardFocus` and `setScrollFocus` extension methods for `Actor`. + - `contains` operator extension method of `Group` and `Stage` supporting `actor in group` syntax. + - `+` and `-` operator for adding actors to `Group` and `Stage`. + - `alpha` extension field for `Actor` and `Stage`. + - Lambda consuming `onChange`, `onClick`, `onKey`, `onScrollFocus` and `onKeyboardFocus` extension methods for `Actor`, allowing to quickly define event listeners. + - `+` and `-` operator extension methods can be used to add `Action` instances to a `Stage`. + - `Action.then` infix extension method can be used to chain actions into sequences. - **[FEATURE]** (`ktx-assets`) Implemented `ktx-assets` module. + - `Assets.manager` global `AssetManager` instance. + - `load` function can be used to load assets asynchronously via the global `AssetManager` instance. `loadOnDemand` can be used to load assets immediately in a blocking manner. `unload` can unload the assets. + - `asset` function can be used to access loaded assets from the global `AssetManager` instance. + - `isLoaded` allows to check if an asset has been loaded by the global `AssetManager`. + - `disposeSafely` and lambda consuming `dispose` were added to `Disposable`. + - `Iterable` and `Array` instances storing `Disposable` elements can now be disposed. + - `Exception.ignore` extension method was added for explicit no-op handling of exceptions. + - `Pool.invoke` operator extension method was added as an alternative to `Pool.obtain`. + - `Pool.invoke(T)` operator extension method was added as an alternative to `Pool.free(T)`. + - Lambda consuming `pool` factory function was added. + - `toClasspathFile`, `toInternalFile`, `toLocalFile`, `toExternalFile` and `toAbsoluteFile` converter methods added to `FileHandle`. + - `file` factory function was added. - **[FEATURE]** (`ktx-collections`) Implemented `ktx-collections` module. + - `Array` factory function `gdxArrayOf` and converter method `toGdxArray`. + - `Array` extensions including: `isEmpty`, `isNotEmpty`, `size`, `+`, `-`, `getLast`, `removeLast`, `get`, `addAll`, `removeAll`, `iterate`. + - `ObjectSet` factory function `gdxSetOf` and converter method `toGdxSet`. + - `ObjectSet` extensions including: `isEmpty`, `isNotEmpty`, `size`, `+`, `-`, `addAll`, `removeAll`, `iterate`. + - `ObjectMap` factory function `gdxMapOf` and `IdentityMap` factory `gdxIdentityMapOf`. + - Maps extensions including: `isEmpty`, `isNotEmpty`, `size`, `contains` (`in`), `set` (`[]`), `iterate`, `toGdxSet`. + - Lambda consuming `Iterable.toGdxMap` allows to convert any collection to a `ObjectMap`. + - `PooledList` collection as an alternative to `PooledLinkedList`. Includes `gdxListOf` and `toGdxList` factory methods. - **[FEATURE]** (`ktx-i18n`) Implemented `ktx-i18n` module. + - `I18n.defaultBundle` global `I18NBundle` instance loaded by `I18n.load`. + - `addListener`, `removeListener` and `clearListeners` of `I18n` allow to handle the lifecycle of the global `I18NBundle`. + - `nls` functions allow to access global `I18NBundle`. + - `I18NBundle.get` operator function improves access to the bundle lines. + - `BundleLine` is an interface designed to be implemented by enums that match bundle line names stored in an i18n properties file. - **[FEATURE]** (`ktx-inject`) Implemented `ktx-inject` module. + - `Context` is the core of the dependency injection framework, storing the registered singletons and providers. + - Global `Context` instance is available via `ContextContainer.defaultContext`. + - `inject` and `provider` functions allow to extract instances and providers of selected type from the global `Context`. + - `register` allows to add singletons and providers to the global `Context`. - **[FEATURE]** (`ktx-log`) Implemented `ktx-log` module. + - `debug`, `info` and `error` functions allow to log data with the LibGDX logging API. + - `logger` factory function provides instances of the KTX `Logger` that wraps LibGDX logging API. - **[FEATURE]** (`ktx-math`) Implemented `ktx-math` module. + - `vec2`, `vec3`, `mat3` and `mat4` factory methods for `Vector2`, `Vector3`, `Matrix3` and `Matrix4` respectively. + - `+`, `-`, `*`, `/`, `-`, `++`, `--`, `<`, `>`, `<=`, `>=` operators support for `Vector2` and `Vector3`. + - `+`, `-`, `*`, `!`, `-` operators support for `Matrix3` and `Matrix4`. + - `Vector2`, `Vector3`, `Matrix3` and `Matrix4` are now decomposable into 2, 3, 9 and 16 components respectively. - **[FEATURE]** (`ktx-scene2d`) Implemented `ktx-scene2d` module. + - Added DSL for constructing complex `Scene2D` widgets. + - Factory methods for parental actors: `buttonTable`, `container`, `dialog`, `horizontalGroup`, `scrollPane`, `splitPane`, `stack`, `table`, `tree`, `verticalGroup` and `window`. + - Factory methods for secondary parental actors: `button`, `checkBox`, `imageButton` and `imageTextButton`. + - Factory methods for child actors: `image`, `label`, `list`, `progressBar`, `selectBox`, `slider`, `textArea`, `textField` and `touchpad`. - **[FEATURE]** (`ktx-style`) Implemented `ktx-style` module. + - `skin` factory methods producing `Skin` instances. + - `get` operator infix function for quick access of `Skin` resources. + - `set` operator function for quick modification of `Skin` resources. + - Factory methods for styles of `Scene2D` widgets: `color`, `button`, `checkBox`, `imageButton`, `imageTextButton`, `label`, `list`, `progressBar`, `selectBox`, `slider`, `splitPane`, `textButton`, `textField`, `textTooltip`, `touchpad`, `tree`, `window`. - **[FEATURE]** (`ktx-vis`) Implemented `ktx-vis` module. + - Added DSL for constructing complex `VisUI` widgets. - **[FEATURE]** (`ktx-vis-style`) Implemented `ktx-vis-style` module. + - Factory methods for styles of `VisUI` widgets. From 8fcfdad70546ba2d90f10c9392bc6412e97ef62f Mon Sep 17 00:00:00 2001 From: MJ Date: Sun, 22 Dec 2019 13:22:02 +0100 Subject: [PATCH 44/45] Added Array.transfer extension method. #232 --- CHANGELOG.md | 2 +- collections/README.md | 3 +- .../src/main/kotlin/ktx/collections/arrays.kt | 22 +++++++- .../test/kotlin/ktx/collections/arraysTest.kt | 52 +++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c901a59b..27263bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ - **[UPDATE]** Updated to Kotlin 1.3.61. - **[UPDATE]** Updated to Kotlin Coroutines 1.3.3. - **[FEATURE]** (`ktx-assets`) Added `AssetGroup` abstract class that allows to manage groups of assets. -- **[FEATURE]** (`ktx-collections`) Added `Array.removeAll` and `Array.retainAll` higher-order in-place filters. +- **[FEATURE]** (`ktx-collections`) Added `removeAll`, `retainAll` and `transfer` extensions to LibGDX `Array` using lambda predicates to modify the array in-place. - **[CHANGE]** (`ktx-collections`) `PooledList` now implements `MutableIterable`. - **[FEATURE]** (`ktx-graphics`) Added `Batch.begin` extension methods that automatically set projection matrix from a `Camera` or `Matrix4`. - **[FEATURE]** (`ktx-style`) Added `Skin` extension methods with reified resource types: `optional`, `add`, `remove`, `has` and `getAll`. diff --git a/collections/README.md b/collections/README.md index 3de9e730..6e190fb5 100644 --- a/collections/README.md +++ b/collections/README.md @@ -32,8 +32,9 @@ chained. - Missing `addAll` and `removeAll` methods for arrays and iterables were added. - `iterate` method allows to iterate over collection's elements, while providing reference to `MutableInterator`. Can be used to easily remove collection elements during iteration. -- `removeAll` and `retainAll` higher-order functions that work like collection extensions in Kotlin stdlib. A Pool can +- `removeAll` and `retainAll` higher-order functions that work like collection extensions in Kotlin stdlib. A `Pool` can optionally be passed to automatically free the removed items. +- `transfer` extension method can be used to move elements from one array to another using a lambda predicate. - `map`, `filter`, `flatten` and `flatMap` methods that work like methods in Kotlin stdlib but return `GdxArray`. - Every iterable and array can be converted to `Array` using `toGdxArray` method. - `IntArray`, `BooleanArray` and `FloatArray` can be converted to corresponding LibGDX primitive collections using diff --git a/collections/src/main/kotlin/ktx/collections/arrays.kt b/collections/src/main/kotlin/ktx/collections/arrays.kt index 2e4d5eae..3982f822 100644 --- a/collections/src/main/kotlin/ktx/collections/arrays.kt +++ b/collections/src/main/kotlin/ktx/collections/arrays.kt @@ -237,7 +237,7 @@ inline fun > GdxArray.sortByDescending(crossin * Removes elements from the array that satisfy the [predicate]. * @param pool Removed items are freed to this pool. */ -inline fun GdxArray.removeAll(pool: Pool?, predicate: (Type) -> Boolean) { +inline fun GdxArray.removeAll(pool: Pool? = null, predicate: (Type) -> Boolean) { var currentWriteIndex = 0 for (i in 0 until size) { val value = items[i] @@ -273,6 +273,26 @@ inline fun GdxArray.retainAll(pool: Pool? = null, predicate: truncate(currentWriteIndex) } +/** + * Transfers elements that match the [predicate] into the selected [toArray]. + * The elements will be removed from this array and added [toArray]. + */ +inline fun GdxArray.transfer(toArray: GdxArray, predicate: (Type) -> Boolean) { + var currentWriteIndex = 0 + for (i in 0 until size) { + val value = items[i] + if (predicate(value)) { + toArray.add(value) + } else { + if (currentWriteIndex != i) { + items[currentWriteIndex] = value + } + currentWriteIndex++ + } + } + truncate(currentWriteIndex) +} + /** * Returns a [GdxArray] containing the results of applying the given [transform] function * to each element in the original [GdxArray]. diff --git a/collections/src/test/kotlin/ktx/collections/arraysTest.kt b/collections/src/test/kotlin/ktx/collections/arraysTest.kt index 708d578e..de530e88 100644 --- a/collections/src/test/kotlin/ktx/collections/arraysTest.kt +++ b/collections/src/test/kotlin/ktx/collections/arraysTest.kt @@ -393,6 +393,58 @@ class ArraysTest { assertEquals(pool.peak, 2) } + @Test + fun `should transfer elements matching predicate`() { + val array = GdxArray.with(0, 1, 2, 3, 4) + val target = GdxArray.with() + + array.transfer(toArray = target) { + it % 2 == 0 + } + + assertEquals(GdxArray.with(1, 3), array) + assertEquals(GdxArray.with(0, 2, 4), target) + } + + @Test + fun `should transfer all elements`() { + val array = GdxArray.with(0, 1, 2, 3, 4) + val target = GdxArray.with() + + array.transfer(toArray = target) { + true + } + + assertEquals(GdxArray.with(), array) + assertEquals(GdxArray.with(0, 1, 2, 3, 4), target) + } + + @Test + fun `should transfer no elements`() { + val array = GdxArray.with(0, 1, 2, 3, 4) + val target = GdxArray.with() + + array.transfer(toArray = target) { + false + } + + assertEquals(GdxArray.with(0, 1, 2, 3, 4), array) + assertEquals(GdxArray.with(), target) + } + + @Test + fun `should not transfer any elements from empty array`() { + val array = GdxArray.with() + val target = GdxArray.with() + + array.transfer(toArray = target) { + it % 2 == 0 + } + + assertEquals(GdxArray.with(), array) + assertEquals(GdxArray.with(), target) + } + @Test fun `should retain elements from existing GdxArray`() { val array = GdxArray.with(1, 2, 3, 4, 5) From 38bc0af43cd75aabbd338b120eef9d9c883bd187 Mon Sep 17 00:00:00 2001 From: MJ Date: Sun, 22 Dec 2019 13:29:37 +0100 Subject: [PATCH 45/45] Changed KTX version to 1.9.10-b3. #231 --- CHANGELOG.md | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27263bfe..6e52c0d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -#### 1.9.10-SNAPSHOT +#### 1.9.10-b3 - **[UPDATE]** Updated to Kotlin 1.3.61. - **[UPDATE]** Updated to Kotlin Coroutines 1.3.3. diff --git a/version.txt b/version.txt index e7b756bc..e2b6eabf 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.9.10-SNAPSHOT +1.9.10-b3