diff --git a/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfig.kt b/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfig.kt index ae94ca4d60..97c1026969 100644 --- a/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfig.kt +++ b/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfig.kt @@ -14,6 +14,8 @@ import kotlin.jvm.JvmStatic * Object that encapsulates the configuration parameters for an [Application]. * This includes properties such as the shape of the cursor, the color of the cursor * and if the cursor should blink or not. + * + * Typically you'll want to construct this using [AppConfigBuilder], not AppConfig's constructor. */ @Suppress("ArrayInDataClass") data class AppConfig( @@ -114,14 +116,33 @@ data class AppConfig( * If set [iconPath] will contain the path of the resource that points * to an icon image that will be used in the application window. */ - val iconPath: String? = null + val iconPath: String? = null, + /** + * If set, contains custom properties that plugin authors can set and access. + */ + internal val customProperties: Map, Any> = emptyMap() ) { /** * Tells whether bounds check should be performed or not. * This depends on the various debug mode configurations. */ - fun shouldCheckBounds() = debugMode.not() || (debugMode && debugConfig.relaxBoundsCheck.not()) + fun shouldCheckBounds() = !debugMode || !debugConfig.relaxBoundsCheck + + /** + * Retrieve a custom property set earlier using [AppConfigBuilder.withProperty]. If this property was + * never set, returns `null`. + * + * ### End Developers + * + * You probably don't need to call this API. + */ + fun getProperty(key: AppConfigKey): T? { + val value: Any? = customProperties[key] + // This is actually a safe cast because of the way `withProperty` is defined. + @Suppress("UNCHECKED_CAST") + return value as T? + } companion object { diff --git a/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfigKey.kt b/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfigKey.kt new file mode 100644 index 0000000000..160bb023a9 --- /dev/null +++ b/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/application/AppConfigKey.kt @@ -0,0 +1,7 @@ +package org.hexworks.zircon.api.application + +/** + * This simple interface is used to set and retrieve custom properties on [AppConfig] + * in a typesafe way. + */ +interface AppConfigKey diff --git a/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/builder/application/AppConfigBuilder.kt b/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/builder/application/AppConfigBuilder.kt index 7af9b79791..7a36f8c16a 100644 --- a/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/builder/application/AppConfigBuilder.kt +++ b/zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/builder/application/AppConfigBuilder.kt @@ -191,6 +191,25 @@ data class AppConfigBuilder( ) } + /** + * Adds a custom property into the AppConfig object. This can later be retrieved using [AppConfig.getProperty]. + * + * ### End Developers + * + * You probably don't need to call this API. + * + * ### Plugin Developers + * + * Write extension methods off of [AppConfigBuilder] that call this API in order to enable end developers + * to pass configuration in through AppConfig that your plugin can later use. It's recommended that [key] + * be an `object` with minimal visibility (e.g. `internal`). + */ + fun withProperty(key: AppConfigKey, value: T): AppConfigBuilder = also { + config = config.copy( + customProperties = config.customProperties + (key to value) + ) + } + @Deprecated("This will be removed in the next version, as the behavior is inconsistent.") fun withFullScreen(screenWidth: Int, screenHeight: Int) = also { throw UnsupportedOperationException("Unstable api, use withFullScreen(true) instead") diff --git a/zircon.core/src/jvmTest/kotlin/org/hexworks/zircon/api/application/AppConfigTest.kt b/zircon.core/src/jvmTest/kotlin/org/hexworks/zircon/api/application/AppConfigTest.kt index dbb99e7d01..ca99ac5bc3 100644 --- a/zircon.core/src/jvmTest/kotlin/org/hexworks/zircon/api/application/AppConfigTest.kt +++ b/zircon.core/src/jvmTest/kotlin/org/hexworks/zircon/api/application/AppConfigTest.kt @@ -5,6 +5,9 @@ import org.hexworks.zircon.api.builder.application.AppConfigBuilder import org.hexworks.zircon.api.color.ANSITileColor import org.junit.Test +private object TestAppConfigKey : AppConfigKey +private object TestAppConfigKey2 : AppConfigKey + class AppConfigTest { @Test @@ -29,6 +32,44 @@ class AppConfigTest { .isEqualTo(HAS_CLIPBOARD) } + @Test + fun propertyUnset() { + val appConfig = AppConfigBuilder.newBuilder().build() + assertThat(appConfig.getProperty(TestAppConfigKey)) + .isNull() + } + + @Test + fun propertySet() { + val appConfig = AppConfigBuilder.newBuilder() + .withProperty(TestAppConfigKey, "foo") + .build() + assertThat(appConfig.getProperty(TestAppConfigKey)) + .isEqualTo("foo") + } + + @Test + fun propertyOverwrite() { + val appConfig = AppConfigBuilder.newBuilder() + .withProperty(TestAppConfigKey, "foo") + .withProperty(TestAppConfigKey, "bar") + .build() + assertThat(appConfig.getProperty(TestAppConfigKey)) + .isEqualTo("bar") + } + + @Test + fun propertyMultiple() { + val appConfig = AppConfigBuilder.newBuilder() + .withProperty(TestAppConfigKey, "foo") + .withProperty(TestAppConfigKey2, "bar") + .build() + assertThat(appConfig.getProperty(TestAppConfigKey)) + .isEqualTo("foo") + assertThat(appConfig.getProperty(TestAppConfigKey2)) + .isEqualTo("bar") + } + companion object { val BLINK_TIME = 5L val CURSOR_STYLE = CursorStyle.UNDER_BAR