diff --git a/composeApp/src/wasmJsMain/kotlin/App.kt b/composeApp/src/wasmJsMain/kotlin/App.kt index bd450e4..e893258 100644 --- a/composeApp/src/wasmJsMain/kotlin/App.kt +++ b/composeApp/src/wasmJsMain/kotlin/App.kt @@ -1,3 +1,4 @@ + import LangType.* import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -10,6 +11,7 @@ import androidx.compose.material.icons.filled.LightMode import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.text.AnnotatedString @@ -20,7 +22,6 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import kotlinx.browser.localStorage -import kotlinx.browser.window import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.Font import org.jetbrains.compose.resources.FontResource @@ -28,11 +29,55 @@ import org.jetbrains.compose.resources.FontResource const val SNAPSHOT_REPO = "https://s01.oss.sonatype.org/content/repositories/snapshots/" const val WIKI_ARCH_LINK = "https://github.com/Over-Run/overrungl/wiki/Installing-Natives#supported-architectures" +@OptIn(ExperimentalResourceApi::class) @Composable fun App() { val initDarkTheme = localStorage.getItem("darkTheme")?.toBoolean() ?: isSystemInDarkTheme() var darkTheme by remember { mutableStateOf(initDarkTheme) } MaterialTheme(colors = if (darkTheme) darkColors() else lightColors()) { + val JetBrainsMono = FontFamily( + Font(FontResource("jetbrainsmono_regular.ttf"), weight = FontWeight.Normal) + ) + + var langType by remember { + mutableStateOf( + langTypeFromString(localStorage.getItem("langType")) ?: GRADLE_KOTLIN + ) + } + var selectedVersion by remember { mutableStateOf(V_LATEST_SNAPSHOT) } + val selectedModules = remember { + mutableStateMapOf().also { + val item = localStorage.getItem("selectedModules") + val initModules = + item?.let { s -> + s.split(',').mapNotNull { m -> bindingFromString(m) } + } ?: emptyList() + selectedVersion.modules.forEach { m -> it[m] = initModules.contains(m) } + it[Binding.CORE] = true + } + } + val selectedNatives = remember { + mutableStateListOf().also { + localStorage.getItem("selectedNatives")?.let { s -> + s.split(',').mapNotNull { n -> nativeFromString(n) } + }?.let(it::addAll) + } + } + val release by remember { mutableStateOf(false) } + var joml by remember { mutableStateOf(localStorage.getItem("joml").toBoolean()) } + var noVariable by remember { mutableStateOf(localStorage.getItem("noVariable").toBoolean()) } + var selectedPreset by remember { mutableStateOf(Preset.CUSTOM) } + + fun generateCode(): String = generatedCode( + langType = langType, + release = release, + modules = selectedModules, + joml = joml, + noVariable = noVariable, + version = selectedVersion, + natives = selectedNatives + ) + Scaffold(topBar = { TopAppBar(title = { Text("OverrunGL Modules Customizer") @@ -50,286 +95,269 @@ fun App() { } } }) + }, bottomBar = { + BottomAppBar { + Button(onClick = { + LocalClipboardManager.current.setText(AnnotatedString(generateCode())) + }) { + Icon(Icons.Filled.ContentCopy, contentDescription = "Copy to clipboard") + Text("Copy to clipboard") + } + /*Button(onClick = { + }) { + Icon(Icons.Filled.Download, contentDescription = "Download Gradle project template") + Text("Download Gradle project template (WIP)") + }*/ + } }) { val scrollState = rememberScrollState() Box(modifier = Modifier.fillMaxSize()) { Surface(modifier = Modifier.verticalScroll(scrollState)) { - Customizer() - } - VerticalScrollbar( - adapter = rememberScrollbarAdapter(scrollState), - modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight() - ) - } - } - } -} - -@OptIn(ExperimentalResourceApi::class) -@Composable -private fun Customizer() { - val JetBrainsMono = FontFamily( - Font(FontResource("jetbrainsmono_regular.ttf"), weight = FontWeight.Normal) - ) - var langType by remember { - mutableStateOf( - langTypeFromString(localStorage.getItem("langType")) ?: GRADLE_KOTLIN - ) - } - var selectedVersion by remember { mutableStateOf(V_LATEST_SNAPSHOT) } - val selectedModules = remember { - mutableStateMapOf().also { - val item = localStorage.getItem("selectedModules") - val initModules = - item?.let { s -> - s.split(',').mapNotNull { m -> bindingFromString(m) } - } ?: emptyList() - selectedVersion.modules.forEach { m -> it[m] = initModules.contains(m) } - it[Binding.CORE] = true - } - } - val selectedNatives = remember { - mutableStateListOf().also { - localStorage.getItem("selectedNatives")?.let { s -> - s.split(',').mapNotNull { n -> nativeFromString(n) } - }?.let(it::addAll) - } - } - val release by remember { mutableStateOf(false) } - var joml by remember { mutableStateOf(localStorage.getItem("joml").toBoolean()) } - var noVariable by remember { mutableStateOf(localStorage.getItem("noVariable").toBoolean()) } - var selectedPreset by remember { mutableStateOf(Preset.CUSTOM) } - - fun storeSelectedNatives() { - localStorage.setItem( - "selectedNatives", - selectedNatives.joinToString(separator = ",") { m -> m.name }) - } + fun storeSelectedNatives() { + localStorage.setItem( + "selectedNatives", + selectedNatives.joinToString(separator = ",") { m -> m.name }) + } - fun generateCode(): String = generatedCode( - langType = langType, - release = release, - modules = selectedModules, - joml = joml, - noVariable = noVariable, - version = selectedVersion, - natives = selectedNatives - ) - - Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { - // disclaimer - /*Text( - "Disclaimer: this customizer uses experimental features and might occur errors when any key is pressed", - fontWeight = FontWeight.Bold - )*/ - - // select the build type - Row { - Button(onClick = { selectedVersion = V_LATEST_SNAPSHOT }, modifier = Modifier.padding(all = 32.dp)) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Snapshot", fontSize = 2.em) - Text("Unstable build", fontStyle = FontStyle.Italic) - Text("$V_LATEST_SNAPSHOT") - } - } - } + //region customizer main body + Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { + // disclaimer + /*Text( + "Disclaimer: this customizer uses experimental features and might occur errors when any key is pressed", + fontWeight = FontWeight.Bold + )*/ - Surface(modifier = Modifier.fillMaxSize().padding(all = 16.dp)) { - // inside - Surface( - modifier = Modifier.fillMaxSize(), - border = BorderStroke(width = 2.dp, color = MaterialTheme.colors.onSurface) - ) { - Column { - Row( - modifier = Modifier.fillMaxWidth() - .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp) - ) { - @Composable - inline fun optionTitle(text: String) { - Text( - text, - fontSize = 1.2.em, - fontWeight = FontWeight.Bold - ) + //region select the build type + Row { + Button( + onClick = { selectedVersion = V_LATEST_SNAPSHOT }, + modifier = Modifier.padding(all = 32.dp) + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("Snapshot", fontSize = 2.em) + Text("Unstable build", fontStyle = FontStyle.Italic) + Text("$V_LATEST_SNAPSHOT") + } + } } + //endregion + + Surface(modifier = Modifier.fillMaxSize().padding(all = 16.dp)) { + // inside + Surface( + modifier = Modifier.fillMaxSize(), + border = BorderStroke(width = 2.dp, color = MaterialTheme.colors.onSurface) + ) { + Column { + Row( + modifier = Modifier.fillMaxWidth() + .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp) + ) { + @Composable + inline fun optionTitle(text: String) { + Text( + text, + 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") - } + 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") - Row(verticalAlignment = Alignment.CenterVertically) { - TriStateCheckbox( - state = if (selectedNatives.isEmpty()) ToggleableState.Off - else if (selectedNatives.size == Native.entries.size) ToggleableState.On - else ToggleableState.Indeterminate, - onClick = { - val size = selectedNatives.size - selectedNatives.clear() - if (size != Native.entries.size) { - selectedNatives.addAll(Native.entries) - } - storeSelectedNatives() - }) - Text("Select/unselect all") - } - Native.entries.forEach { native -> - Row(verticalAlignment = Alignment.CenterVertically) { - Checkbox( - checked = selectedNatives.contains(native), - onCheckedChange = { - if (it) { - selectedNatives.add(native) - } else { - selectedNatives.remove(native) + // natives + optionTitle("Natives") + Row(verticalAlignment = Alignment.CenterVertically) { + TriStateCheckbox( + state = if (selectedNatives.isEmpty()) ToggleableState.Off + else if (selectedNatives.size == Native.entries.size) ToggleableState.On + else ToggleableState.Indeterminate, + onClick = { + val size = selectedNatives.size + selectedNatives.clear() + if (size != Native.entries.size) { + selectedNatives.addAll(Native.entries) + } + storeSelectedNatives() + }) + Text("Select/unselect all") } - storeSelectedNatives() + Native.entries.forEach { native -> + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = selectedNatives.contains(native), + onCheckedChange = { + if (it) { + selectedNatives.add(native) + } else { + selectedNatives.remove(native) + } + storeSelectedNatives() + } + ) + if (native.macos) { + Icon(Icons.Filled.Apple, contentDescription = null) + } else if (native.linux) { + Icon(Icons.Filled.Linux, contentDescription = null) + } else if (native.windows) { + Icon(Icons.Filled.Microsoft, contentDescription = null) + } + Text(native.description) + } + } + val uriHandler = LocalUriHandler.current + ClickableText( + AnnotatedString( + "Check supported architecture for each module here", + spanStyle = MaterialTheme.typography.button.toSpanStyle() + .copy(textDecoration = TextDecoration.Underline) + ), onClick = { + uriHandler.openUri(WIKI_ARCH_LINK) + } + ) } - ) - if (native.macos) { - Icon(Icons.Filled.Apple, contentDescription = null) - } else if (native.linux) { - Icon(Icons.Filled.Linux, contentDescription = null) - } else if (native.windows) { - Icon(Icons.Filled.Microsoft, contentDescription = null) - } - Text(native.description) - } - } - val uriHandler = LocalUriHandler.current - ClickableText( - AnnotatedString( - "Check supported architecture for each module here", - spanStyle = MaterialTheme.typography.button.toSpanStyle() - .copy(textDecoration = TextDecoration.Underline) - ), onClick = { - uriHandler.openUri(WIKI_ARCH_LINK) - } - ) - } - Column(modifier = Modifier.padding(horizontal = 80.dp)) { - // presets - optionTitle("Presets") - Preset.entries.forEach { - Row(verticalAlignment = Alignment.CenterVertically) { - RadioButton(selected = it == selectedPreset, onClick = { - selectedPreset = it - if (it.modules != null) { - selectedVersion.modules.forEach { m -> - selectedModules[m] = it.modules.contains(m) + Column(modifier = Modifier.padding(horizontal = 80.dp)) { + // presets + optionTitle("Presets") + Preset.entries.forEach { + Row(verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected = it == selectedPreset, onClick = { + selectedPreset = it + if (it.modules != null) { + selectedVersion.modules.forEach { m -> + selectedModules[m] = it.modules.contains(m) + } + } + }, enabled = it != Preset.CUSTOM) + Text(it.text) + } } - } - }, enabled = it != Preset.CUSTOM) - Text(it.text) - } - } - // addons - optionTitle("Addons") - Row(verticalAlignment = Alignment.CenterVertically) { - Checkbox(checked = joml, onCheckedChange = { - joml = it - localStorage.setItem("joml", it.toString()) - }) - Text("JOML v$V_JOML") - } + // addons + optionTitle("Addons") + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(checked = joml, onCheckedChange = { + joml = it + localStorage.setItem("joml", it.toString()) + }) + Text("JOML v$V_JOML") + } - // version + // version // optionTitle("Version") - } + } - Column { - // modules - optionTitle("Modules") - selectedVersion.modules.forEach { module -> - Row(verticalAlignment = Alignment.CenterVertically) { - Checkbox( - checked = if (module.nonSelectable) true else selectedModules[module] ?: false, - enabled = !module.nonSelectable, - onCheckedChange = { - selectedPreset = Preset.CUSTOM - selectedModules[module] = it - localStorage.setItem( - "selectedModules", - selectedModules.filterValues { b -> b }.keys.joinToString(separator = ",") { m -> m.name }) - }) - Text(module.moduleName) - } - } - } - } + Column { + // modules + optionTitle("Modules") + selectedVersion.modules.forEach { module -> + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = if (module.nonSelectable) true else selectedModules[module] + ?: false, + enabled = !module.nonSelectable, + onCheckedChange = { + selectedPreset = Preset.CUSTOM + selectedModules[module] = it + localStorage.setItem( + "selectedModules", + selectedModules.filterValues { b -> b }.keys.joinToString( + separator = "," + ) { m -> m.name }) + }) + Text(module.moduleName) + } + } + } + } - // generated code - Column(modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 16.dp)) { - Row { - @Composable - fun langTypeButton(buttonLangType: LangType) { - OutlinedButton( - onClick = { - langType = buttonLangType - localStorage.setItem("langType", buttonLangType.name) - }, - modifier = if (buttonLangType == langType) Modifier.offset(y = 8.dp) else Modifier - ) { - Text(buttonLangType.typeName) - } - } + //region generated code + Column( + modifier = Modifier.padding( + start = 16.dp, + top = 8.dp, + end = 16.dp, + bottom = 16.dp + ) + ) { + Row { + @Composable + fun langTypeButton(buttonLangType: LangType) { + OutlinedButton( + onClick = { + langType = buttonLangType + localStorage.setItem("langType", buttonLangType.name) + }, + modifier = if (buttonLangType == langType) Modifier.offset(y = 8.dp) else Modifier + ) { + Text(buttonLangType.typeName) + } + } - langTypeButton(GRADLE_KOTLIN) - langTypeButton(GRADLE_GROOVY) - langTypeButton(GRADLE_CATALOG) - langTypeButton(MAVEN) - langTypeButton(VM_OPTION) - langTypeButton(MANIFEST_ATTRIB) - } - Surface( - modifier = Modifier.fillMaxWidth(), - border = ButtonDefaults.outlinedBorder - ) { + langTypeButton(GRADLE_KOTLIN) + langTypeButton(GRADLE_GROOVY) + langTypeButton(GRADLE_CATALOG) + langTypeButton(MAVEN) + langTypeButton(VM_OPTION) + langTypeButton(MANIFEST_ATTRIB) + } + Surface( + modifier = Modifier.fillMaxWidth(), + border = ButtonDefaults.outlinedBorder + ) { // SelectionContainer { - Text( - generateCode(), - modifier = Modifier.padding(4.dp), - fontFamily = JetBrainsMono - ) + Text( + generateCode(), + modifier = Modifier.padding( + start = 4.dp, + top = 4.dp, + end = 4.dp, + bottom = 100.dp + ), + fontFamily = JetBrainsMono + ) // } - } - 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 - ) - } + } + 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.") - } + 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.") - } + MANIFEST_ATTRIB -> { + Text("Add this attribute to META-INF/MANIFEST.MF to allow code in executable JAR files to call restricted methods.") + } - else -> {} - } - Button(onClick = { - window.navigator.clipboard.writeText(generateCode()) - }) { - Icon(Icons.Default.ContentCopy, contentDescription = null) - Text("Copy to clipboard") + else -> {} + } + } + //endregion + } + } } } + //endregion } + + VerticalScrollbar( + adapter = rememberScrollbarAdapter(scrollState), + modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight() + ) } } } diff --git a/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/Download.kt b/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/Download.kt new file mode 100644 index 0000000..2078197 --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/androidx/compose/material/icons/filled/Download.kt @@ -0,0 +1,51 @@ +/* + * 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.Download: ImageVector + get() { + if (_download != null) { + return _download!! + } + _download = materialIcon(name = "Filled.Download") { + materialPath { + moveTo(5.0f, 20.0f) + horizontalLineToRelative(14.0f) + verticalLineToRelative(-2.0f) + horizontalLineTo(5.0f) + verticalLineTo(20.0f) + close() + moveTo(19.0f, 9.0f) + horizontalLineToRelative(-4.0f) + verticalLineTo(3.0f) + horizontalLineTo(9.0f) + verticalLineToRelative(6.0f) + horizontalLineTo(5.0f) + lineToRelative(7.0f, 7.0f) + lineTo(19.0f, 9.0f) + close() + } + } + return _download!! + } + +private var _download: ImageVector? = null