diff --git a/composeApp/src/wasmJsMain/kotlin/App.kt b/composeApp/src/wasmJsMain/kotlin/App.kt index c0cf4ab..4f4b5cd 100644 --- a/composeApp/src/wasmJsMain/kotlin/App.kt +++ b/composeApp/src/wasmJsMain/kotlin/App.kt @@ -1,10 +1,11 @@ -import LangType.GRADLE_GROOVY -import LangType.GRADLE_KOTLIN -import androidx.compose.foundation.BorderStroke +import LangType.* +import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ContentCopy +import androidx.compose.material.icons.filled.DarkMode +import androidx.compose.material.icons.filled.LightMode import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -19,17 +20,40 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.Font import org.jetbrains.compose.resources.FontResource +const val SNAPSHOT_REPO = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + @Composable fun App() { - MaterialTheme { + val initDarkTheme = localStorage.getItem("darkTheme")?.toBoolean() ?: isSystemInDarkTheme() + var darkTheme by remember { mutableStateOf(initDarkTheme) } + MaterialTheme(colors = if (darkTheme) darkColors() else lightColors()) { Scaffold(topBar = { TopAppBar(title = { Text("OverrunGL Modules Customizer") Text("v0.3.0", fontSize = 0.8.em) - }) + }, + actions = { + IconToggleButton(checked = darkTheme, onCheckedChange = { + darkTheme = it + localStorage.setItem("darkTheme", it.toString()) + }) { + if (darkTheme) { + Icon(Icons.Filled.LightMode, contentDescription = "Switch to light theme") + } else { + Icon(Icons.Filled.DarkMode, contentDescription = "Switch to dark theme") + } + } + }) }) { - Surface(modifier = Modifier.fillMaxSize()) { - Customizer() + val scrollState = rememberScrollState() + Box(modifier = Modifier.fillMaxSize()) { + Surface(modifier = Modifier.verticalScroll(scrollState)) { + Customizer() + } + VerticalScrollbar( + adapter = rememberScrollbarAdapter(scrollState), + modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight() + ) } } } @@ -41,17 +65,35 @@ private fun Customizer() { val JetBrainsMono = FontFamily( Font(FontResource("jetbrainsmono_regular.ttf"), weight = FontWeight.Normal) ) - var langType by remember { + val (langType, setLangType) = remember { mutableStateOf( langTypeFromString(localStorage.getItem("langType")) ?: GRADLE_KOTLIN ) } - val generatedCode = remember { - when (langType) { - GRADLE_GROOVY -> generateGroovy() - GRADLE_KOTLIN -> generateKotlin() + val selectedModules = remember { + mutableStateListOf().also { + val item = localStorage.getItem("selectedModules") + if (item != null) { + item.let { s -> + s.split(',').mapNotNull { m -> bindingFromString(m) } + }.let(it::addAll) + } else { + it.add(Binding.CORE) + } } } + val release by remember { mutableStateOf(false) } + var joml by remember { mutableStateOf(localStorage.getItem("joml").toBoolean()) } + var noVariable by remember { mutableStateOf(localStorage.getItem("noVariable").toBoolean()) } + + fun generateCode(): String = generatedCode( + langType = langType, + release = release, + modules = selectedModules, + joml = joml, + noVariable = noVariable, + version = "$V_LATEST_SNAPSHOT-SNAPSHOT" + ) Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { // disclaimer @@ -66,7 +108,7 @@ private fun Customizer() { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text("Snapshot", fontSize = 2.em) Text("Unstable build", fontStyle = FontStyle.Italic) - Text("$V_LATEST-SNAPSHOT") + Text("$V_LATEST_SNAPSHOT-SNAPSHOT") } } } @@ -81,72 +123,304 @@ private fun Customizer() { Row(modifier = Modifier.fillMaxWidth().padding(all = 16.dp)) { @Composable inline fun optionTitle(text: String) { - Text(text, fontSize = 1.2.em, fontWeight = FontWeight.Bold) + Text( + text, + modifier = Modifier.padding(start = 14.dp), + fontSize = 1.2.em, + fontWeight = FontWeight.Bold + ) } Column { + // options + optionTitle("Options") + Row(verticalAlignment = Alignment.CenterVertically) { + Switch(checked = noVariable, onCheckedChange = { + noVariable = it + localStorage.setItem("noVariable", it.toString()) + }) + Text("Do not use variables") + } + // natives optionTitle("Natives") } - Column(modifier = Modifier.padding(start = 16.dp, end = 16.dp)) { + Column(modifier = Modifier.padding(horizontal = 80.dp)) { // addons optionTitle("Addons") + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(checked = joml, onCheckedChange = { + joml = it + localStorage.setItem("joml", it.toString()) + }) + Text("JOML v$V_JOML") + } // version - optionTitle("Version") +// optionTitle("Version") } Column { // modules optionTitle("Modules") + Binding.entries.forEach { module -> + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = if (module.nonSelectable) true else selectedModules.contains(module), + enabled = !module.nonSelectable, + onCheckedChange = { + if (it) { + selectedModules.add(module) + } else { + selectedModules.remove(module) + } + localStorage.setItem( + "selectedModules", + selectedModules.joinToString(separator = ",") { m -> m.name }) + }) + Text(module.moduleName) + } + } } } // generated code Column(modifier = Modifier.padding(all = 16.dp)) { Row { - langTypeButton(GRADLE_GROOVY) { langType = it } - langTypeButton(GRADLE_KOTLIN) { langType = it } + langTypeButton(GRADLE_KOTLIN, setLangType) + langTypeButton(GRADLE_GROOVY, setLangType) + langTypeButton(GRADLE_CATALOG, setLangType) + langTypeButton(MAVEN, setLangType) + langTypeButton(VM_OPTION, setLangType) + langTypeButton(MANIFEST_ATTRIB, setLangType) } - Surface( - modifier = Modifier.fillMaxWidth(), - border = ButtonDefaults.outlinedBorder - ) { -// SelectionContainer { - Text( - generatedCode, modifier = Modifier.padding(2.dp), fontFamily = JetBrainsMono - ) -// } + Column { + when (langType) { + GRADLE_CATALOG -> { + Text( + "The Gradle version catalog does NOT support classifier. You have to add the native libraries by yourself.", + fontWeight = FontWeight.Bold + ) + } + + VM_OPTION -> { + Text("Add this VM option to allow OverrunGL to call restricted methods. You might need to add the module name of your application to it.") + } + + MANIFEST_ATTRIB -> { + Text("Add this attribute to META-INF/MANIFEST.MF to allow code in executable JAR files to call restricted methods.") + } + + else -> {} + } + + + Surface( + modifier = Modifier.fillMaxWidth(), + border = ButtonDefaults.outlinedBorder + ) { +// SelectionContainer { + Text( + generateCode(), + modifier = Modifier.padding(2.dp), + fontFamily = JetBrainsMono + ) +// } + } } Button(onClick = { - window.navigator.clipboard.writeText(generatedCode) + window.navigator.clipboard.writeText(generateCode()) }) { Icon(Icons.Default.ContentCopy, contentDescription = null) Text("Copy to clipboard") } } } - } } } } -fun generateGroovy(): String = buildString { - appendLine("dependencies {") - append("}") -} +fun generatedCode( + langType: LangType, + release: Boolean, + modules: List, + joml: Boolean, + noVariable: Boolean, + version: String +): String = buildString { + fun StringBuilder.gradleDependencies() { + appendLine("dependencies {") + appendLine(" implementation(platform(\"io.github.over-run:overrungl-bom:${if (noVariable) version else "\$overrunglVersion"}\"))") + modules.forEach { + appendLine(" implementation(\"io.github.over-run:${it.artifactName}\")") + } + if (joml) { + appendLine(" implementation(\"io.github.over-run:overrungl-joml\")") + appendLine(" implementation(\"org.joml:joml:${if (noVariable) V_JOML else "\$jomlVersion"}\")") + } + append("}") + } -fun generateKotlin(): String = buildString { - appendLine("dependencies {") - append("}") + when (langType) { + GRADLE_GROOVY -> { + if (!noVariable) { + appendLine("project.ext.overrunglVersion = \"$version\"") + if (joml) { + appendLine("project.ext.jomlVersion = \"$V_JOML\"") + } + appendLine() + } + + appendLine("repositories {") + appendLine(" mavenCentral()") + if (!release) { + appendLine(" maven { url \"$SNAPSHOT_REPO\" }") + } + appendLine("}") + appendLine() + + gradleDependencies() + } + + GRADLE_KOTLIN -> { + if (!noVariable) { + appendLine("val overrunglVersion = \"$version\"") + if (joml) { + appendLine("val jomlVersion = \"$V_JOML\"") + } + appendLine() + } + + appendLine("repositories {") + appendLine(" mavenCentral()") + if (!release) { + appendLine(" maven(\"$SNAPSHOT_REPO\")") + } + appendLine("}") + appendLine() + + gradleDependencies() + } + + GRADLE_CATALOG -> { + appendLine("[versions]") + appendLine("overrungl = \"$version\"") + if (joml && !noVariable) { + appendLine("joml = \"$V_JOML\"") + } + appendLine() + + appendLine("[libraries]") + modules.forEach { + appendLine("${it.catalogName} = { module = \"io.github.over-run:${it.artifactName}\", version.ref = \"overrungl\" }") + } + if (joml) { + appendLine("overrungl-joml = { module = \"io.github.over-run:overrungl-joml\", version.ref = \"overrungl\" }") + appendLine("joml = { module = \"org.joml:joml\", version${if (noVariable) " = \"$V_JOML\"" else ".ref = \"joml\""} }") + } + appendLine() + + appendLine("[bundles]") + append( + "overrungl = [${ + buildString { + append(modules.joinToString { "\"${it.catalogName}\"" }) + if (joml) { + append(", \"overrungl-joml\", \"joml\"") + } + } + }]" + ) + } + + MAVEN -> { + if (!noVariable) { + appendLine("") + appendLine(" $version") + if (joml) { + appendLine(" $V_JOML") + } + appendLine("") + appendLine() + } + + appendLine("") + appendLine("") + appendLine() + + if (!release) { + appendLine( + """ + + + sonatype-snapshots + $SNAPSHOT_REPO + false + true + + + """.trimIndent() + ) + } + + appendLine( + """ + + + + io.github.over-run + overrungl-bom + ${if (noVariable) version else "\${overrungl.version}"} + import + pom + + + + """.trimIndent() + ) + + appendLine("") + modules.forEach { + appendLine( + """ + | + | io.github.over-run + | ${it.artifactName} + | + """.trimMargin() + ) + } + if (joml) { + appendLine( + """ + | + | org.joml + | joml + | ${if (noVariable) V_JOML else "\${joml.version}"} + | + """.trimMargin() + ) + } + append("") + } + + VM_OPTION -> { + append("--enable-native-access=io.github.overrun.marshal") + append(modules.joinToString(separator = ",", prefix = ",") { it.javaModuleName }) + } + + MANIFEST_ATTRIB -> { + append("Enable-Native-Access: ALL-UNNAMED") + } + } } @Composable -fun langTypeButton(langType: LangType, onClick: (LangType) -> Unit) { +fun langTypeButton(langType: LangType, setLangType: (LangType) -> Unit) { OutlinedButton(onClick = { - onClick(langType) + setLangType(langType) localStorage.setItem("langType", langType.name) }) { Text(langType.typeName) diff --git a/composeApp/src/wasmJsMain/kotlin/Binding.kt b/composeApp/src/wasmJsMain/kotlin/Binding.kt index 3b2c43e..446129c 100644 --- a/composeApp/src/wasmJsMain/kotlin/Binding.kt +++ b/composeApp/src/wasmJsMain/kotlin/Binding.kt @@ -2,5 +2,24 @@ * @author squid233 * @since 0.3.0 */ -enum class Binding { +enum class Binding( + val moduleName: String, + val artifactName: String, + val javaModuleName: String, + val catalogName: String, + val nonSelectable: Boolean = false +) { + CORE("OverrunGL Core", "overrungl", "overrungl.core", "overrungl-core", nonSelectable = true), + GLFW("GLFW", "overrungl-glfw", "overrungl.glfw", "overrungl-glfw"), + NFD("Native File Dialog", "overrungl-nfd", "overrungl.nfd", "overrungl-nfd"), + OPENGL("OpenGL", "overrungl-opengl", "overrungl.opengl", "overrungl-opengl"), + STB("stb", "overrungl-stb", "overrungl.stb", "overrungl-stb"), +} + +fun bindingFromString(name: String?): Binding? = name?.let { + try { + Binding.valueOf(it) + } catch (e: Exception) { + null + } } diff --git a/composeApp/src/wasmJsMain/kotlin/LangType.kt b/composeApp/src/wasmJsMain/kotlin/LangType.kt index 725b365..5083ff8 100644 --- a/composeApp/src/wasmJsMain/kotlin/LangType.kt +++ b/composeApp/src/wasmJsMain/kotlin/LangType.kt @@ -5,10 +5,16 @@ enum class LangType(val typeName: String) { GRADLE_GROOVY("Gradle (Groovy)"), GRADLE_KOTLIN("Gradle (Kotlin)"), + GRADLE_CATALOG("Gradle (Catalog)"), + MAVEN("Maven"), + VM_OPTION("VM Option"), + MANIFEST_ATTRIB("JAR-file manifest attribute") } -fun langTypeFromString(name: String?): LangType? = when (name) { - "GRADLE_GROOVY" -> LangType.GRADLE_GROOVY - "GRADLE_KOTLIN" -> LangType.GRADLE_KOTLIN - else -> null +fun langTypeFromString(name: String?): LangType? = name?.let { + try { + LangType.valueOf(it) + } catch (e: Exception) { + null + } } diff --git a/composeApp/src/wasmJsMain/kotlin/Version.kt b/composeApp/src/wasmJsMain/kotlin/Version.kt index 98476f3..f7b6bba 100644 --- a/composeApp/src/wasmJsMain/kotlin/Version.kt +++ b/composeApp/src/wasmJsMain/kotlin/Version.kt @@ -8,4 +8,6 @@ class Version(val versionName: String) { val V0_1_0 = Version("0.1.0") -val V_LATEST = V0_1_0 +val V_LATEST_SNAPSHOT = V0_1_0 + +const val V_JOML = "1.10.5" diff --git a/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/DarkMode.kt b/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/DarkMode.kt new file mode 100644 index 0000000..6a94053 --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/DarkMode.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.compose.material.icons.filled + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.material.icons.materialPath +import androidx.compose.ui.graphics.vector.ImageVector + +val Icons.Filled.DarkMode: ImageVector + get() { + if (_darkMode != null) { + return _darkMode!! + } + _darkMode = materialIcon(name = "Filled.DarkMode") { + materialPath { + moveTo(12.0f, 3.0f) + curveToRelative(-4.97f, 0.0f, -9.0f, 4.03f, -9.0f, 9.0f) + reflectiveCurveToRelative(4.03f, 9.0f, 9.0f, 9.0f) + reflectiveCurveToRelative(9.0f, -4.03f, 9.0f, -9.0f) + curveToRelative(0.0f, -0.46f, -0.04f, -0.92f, -0.1f, -1.36f) + curveToRelative(-0.98f, 1.37f, -2.58f, 2.26f, -4.4f, 2.26f) + curveToRelative(-2.98f, 0.0f, -5.4f, -2.42f, -5.4f, -5.4f) + curveToRelative(0.0f, -1.81f, 0.89f, -3.42f, 2.26f, -4.4f) + curveTo(12.92f, 3.04f, 12.46f, 3.0f, 12.0f, 3.0f) + lineTo(12.0f, 3.0f) + close() + } + } + return _darkMode!! + } + +private var _darkMode: ImageVector? = null diff --git a/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/LightMode.kt b/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/LightMode.kt new file mode 100644 index 0000000..865653d --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/LightMode.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.compose.material.icons.filled + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.material.icons.materialPath +import androidx.compose.ui.graphics.vector.ImageVector + +val Icons.Filled.LightMode: ImageVector + get() { + if (_lightMode != null) { + return _lightMode!! + } + _lightMode = materialIcon(name = "Filled.LightMode") { + materialPath { + moveTo(12.0f, 7.0f) + curveToRelative(-2.76f, 0.0f, -5.0f, 2.24f, -5.0f, 5.0f) + reflectiveCurveToRelative(2.24f, 5.0f, 5.0f, 5.0f) + reflectiveCurveToRelative(5.0f, -2.24f, 5.0f, -5.0f) + reflectiveCurveTo(14.76f, 7.0f, 12.0f, 7.0f) + lineTo(12.0f, 7.0f) + close() + moveTo(2.0f, 13.0f) + lineToRelative(2.0f, 0.0f) + curveToRelative(0.55f, 0.0f, 1.0f, -0.45f, 1.0f, -1.0f) + reflectiveCurveToRelative(-0.45f, -1.0f, -1.0f, -1.0f) + lineToRelative(-2.0f, 0.0f) + curveToRelative(-0.55f, 0.0f, -1.0f, 0.45f, -1.0f, 1.0f) + reflectiveCurveTo(1.45f, 13.0f, 2.0f, 13.0f) + close() + moveTo(20.0f, 13.0f) + lineToRelative(2.0f, 0.0f) + curveToRelative(0.55f, 0.0f, 1.0f, -0.45f, 1.0f, -1.0f) + reflectiveCurveToRelative(-0.45f, -1.0f, -1.0f, -1.0f) + lineToRelative(-2.0f, 0.0f) + curveToRelative(-0.55f, 0.0f, -1.0f, 0.45f, -1.0f, 1.0f) + reflectiveCurveTo(19.45f, 13.0f, 20.0f, 13.0f) + close() + moveTo(11.0f, 2.0f) + verticalLineToRelative(2.0f) + curveToRelative(0.0f, 0.55f, 0.45f, 1.0f, 1.0f, 1.0f) + reflectiveCurveToRelative(1.0f, -0.45f, 1.0f, -1.0f) + verticalLineTo(2.0f) + curveToRelative(0.0f, -0.55f, -0.45f, -1.0f, -1.0f, -1.0f) + reflectiveCurveTo(11.0f, 1.45f, 11.0f, 2.0f) + close() + moveTo(11.0f, 20.0f) + verticalLineToRelative(2.0f) + curveToRelative(0.0f, 0.55f, 0.45f, 1.0f, 1.0f, 1.0f) + reflectiveCurveToRelative(1.0f, -0.45f, 1.0f, -1.0f) + verticalLineToRelative(-2.0f) + curveToRelative(0.0f, -0.55f, -0.45f, -1.0f, -1.0f, -1.0f) + curveTo(11.45f, 19.0f, 11.0f, 19.45f, 11.0f, 20.0f) + close() + moveTo(5.99f, 4.58f) + curveToRelative(-0.39f, -0.39f, -1.03f, -0.39f, -1.41f, 0.0f) + curveToRelative(-0.39f, 0.39f, -0.39f, 1.03f, 0.0f, 1.41f) + lineToRelative(1.06f, 1.06f) + curveToRelative(0.39f, 0.39f, 1.03f, 0.39f, 1.41f, 0.0f) + reflectiveCurveToRelative(0.39f, -1.03f, 0.0f, -1.41f) + lineTo(5.99f, 4.58f) + close() + moveTo(18.36f, 16.95f) + curveToRelative(-0.39f, -0.39f, -1.03f, -0.39f, -1.41f, 0.0f) + curveToRelative(-0.39f, 0.39f, -0.39f, 1.03f, 0.0f, 1.41f) + lineToRelative(1.06f, 1.06f) + curveToRelative(0.39f, 0.39f, 1.03f, 0.39f, 1.41f, 0.0f) + curveToRelative(0.39f, -0.39f, 0.39f, -1.03f, 0.0f, -1.41f) + lineTo(18.36f, 16.95f) + close() + moveTo(19.42f, 5.99f) + curveToRelative(0.39f, -0.39f, 0.39f, -1.03f, 0.0f, -1.41f) + curveToRelative(-0.39f, -0.39f, -1.03f, -0.39f, -1.41f, 0.0f) + lineToRelative(-1.06f, 1.06f) + curveToRelative(-0.39f, 0.39f, -0.39f, 1.03f, 0.0f, 1.41f) + reflectiveCurveToRelative(1.03f, 0.39f, 1.41f, 0.0f) + lineTo(19.42f, 5.99f) + close() + moveTo(7.05f, 18.36f) + curveToRelative(0.39f, -0.39f, 0.39f, -1.03f, 0.0f, -1.41f) + curveToRelative(-0.39f, -0.39f, -1.03f, -0.39f, -1.41f, 0.0f) + lineToRelative(-1.06f, 1.06f) + curveToRelative(-0.39f, 0.39f, -0.39f, 1.03f, 0.0f, 1.41f) + reflectiveCurveToRelative(1.03f, 0.39f, 1.41f, 0.0f) + lineTo(7.05f, 18.36f) + close() + } + } + return _lightMode!! + } + +private var _lightMode: ImageVector? = null diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6cdb661..5d82599 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] compose = "1.6.1" -compose-plugin = "1.6.0-rc01" +compose-plugin = "1.6.0-rc02" junit = "4.13.2" -kotlin = "1.9.21" +kotlin = "1.9.22" [libraries] kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }