diff --git a/build.gradle b/build.gradle index eb0ada2..9660076 100644 --- a/build.gradle +++ b/build.gradle @@ -7,10 +7,10 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" + classpath libs.buildscript.kt.gradle + classpath libs.buildscript.kt.serialization + classpath libs.buildscript.jb.dokka + classpath libs.buildscript.gh.release } } diff --git a/core/build.gradle b/core/build.gradle index e98b3fd..6d34000 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,6 +1,7 @@ plugins { id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.plugin.serialization" + alias(libs.plugins.jb.compose) } apply from: "$mppProjectWithSerializationPresetPath" @@ -9,10 +10,16 @@ kotlin { sourceSets { commonMain { dependencies { - api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" - api "dev.inmo:micro_utils.coroutines:$micro_utils_version" - - api "io.ktor:ktor-client-core:$ktor_version" + api libs.kt.coroutines + api libs.microutils.common + api libs.microutils.coroutines + api libs.ktor.client + api(compose.runtime) + } + } + jvmMain { + dependencies { + implementation(compose.desktop.currentOs) } } } diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/BuilderView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/BuilderView.kt new file mode 100644 index 0000000..5c1259f --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/BuilderView.kt @@ -0,0 +1,28 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.* +import dev.inmo.kmppscriptbuilder.core.models.Config +import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer + +expect object BuilderViewDrawer : Drawer + +class BuilderView : View() { + internal val projectTypeView = ProjectTypeView() + internal val mavenInfoView = MavenInfoView() + internal val licensesView = LicensesView() + + internal var saveAvailableState by mutableStateOf(false) + var config: Config + get() = Config(licensesView.licenses, mavenInfoView.mavenConfig, projectTypeView.projectType) + set(value) { + licensesView.licenses = value.licenses + mavenInfoView.mavenConfig = value.mavenConfig + projectTypeView.projectType = value.type + saveAvailableState = true + } + + @Composable + override fun build() { + with(BuilderViewDrawer) { draw() } + } +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/DevelopersView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/DevelopersView.kt new file mode 100644 index 0000000..b22b7a3 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/DevelopersView.kt @@ -0,0 +1,29 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.* +import dev.inmo.kmppscriptbuilder.core.models.Developer +import dev.inmo.kmppscriptbuilder.desktop.utils.CommonTextField + +class DeveloperState( + id: String = "", + name: String = "", + eMail: String = "" +) { + var id: String by mutableStateOf(id) + var name: String by mutableStateOf(name) + var eMail: String by mutableStateOf(eMail) + + fun toDeveloper() = Developer(id, name, eMail) +} + +private fun Developer.toDeveloperState() = DeveloperState(id, name, eMail) + +class DevelopersView : ListView("Developers info") { + var developers = mutableStateListOf() + + override val addItemText: String = "Add developer" + override val removeItemText: String = "Remove developer" + + override fun createItem(): DeveloperState = DeveloperState() + +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/LicensesView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/LicensesView.kt new file mode 100644 index 0000000..72677f4 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/LicensesView.kt @@ -0,0 +1,88 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.* +import dev.inmo.kmppscriptbuilder.core.models.License +import dev.inmo.kmppscriptbuilder.core.models.getLicenses +import io.ktor.client.HttpClient +import kotlinx.coroutines.* + +private class LicenseState( + id: String = "", + title: String = "", + url: String? = null +) { + var id: String by mutableStateOf(id) + var title: String by mutableStateOf(title) + var url: String? by mutableStateOf(url) + + fun toLicense() = License(id, title, url) +} + +private fun License.toLicenseState() = LicenseState(id, title, url) + +class LicensesView: VerticalView("Licenses") { + private var licensesListState = mutableStateListOf() + var licenses: List + get() = licensesListState.map { it.toLicense() } + set(value) { + licensesListState.clear() + licensesListState.addAll(value.map { it.toLicenseState() }) + } + private val availableLicensesState = mutableStateListOf() + private val licensesOffersToShow = mutableStateListOf() + private var licenseSearchFilter by mutableStateOf("") + + init { + CoroutineScope(Dispatchers.Default).launch { + val client = HttpClient() + availableLicensesState.addAll(client.getLicenses().values) + client.close() + } + } + + override val content: @Composable ColumnScope.() -> Unit = { + CommonTextField(licenseSearchFilter, "Search filter") { filterText -> + licenseSearchFilter = filterText + licensesOffersToShow.clear() + if (licenseSearchFilter.isNotEmpty()) { + licensesOffersToShow.addAll( + availableLicensesState.filter { filterText.all { symbol -> symbol.lowercaseChar() in it.title } } + ) + } + } + Column { + licensesOffersToShow.forEach { + Column(Modifier.padding(16.dp, 8.dp, 8.dp, 8.dp)) { + CommonText(it.title, Modifier.clickable { + licensesListState.add(it.toLicenseState()) + licenseSearchFilter = "" + licensesOffersToShow.clear() + }) + Divider() + } + } + } + Button({ licensesListState.add(LicenseState()) }, Modifier.padding(8.dp)) { + CommonText("Add empty license") + } + licensesListState.forEach { license -> + Column(Modifier.padding(8.dp)) { + CommonTextField( + license.id, + "License ID" + ) { license.id = it } + CommonTextField( + license.title, + "License title" + ) { license.title = it } + CommonTextField( + license.url ?: "", + "License URL" + ) { license.url = it } + Button({ licensesListState.remove(license) }, Modifier.padding(8.dp)) { + CommonText("Remove") + } + } + } + } +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ListView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ListView.kt new file mode 100644 index 0000000..f1aa590 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ListView.kt @@ -0,0 +1,26 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer + +expect class ListViewDrawer() : Drawer> + +abstract class ListView(protected val title: String) : View() { + internal val itemsList = mutableStateListOf() + + internal open val addItemText: String = "Add" + internal open val removeItemText: String = "Remove" + + internal abstract fun createItem(): T + @Composable + internal abstract fun buildView(item: T) + + protected val drawer = ListViewDrawer() + + override fun build() { + DrawVertically(title) { + with(drawer) { draw() } + } + } +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/MavenInfoView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/MavenInfoView.kt new file mode 100644 index 0000000..d1626d8 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/MavenInfoView.kt @@ -0,0 +1,104 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.VerticalAlignmentLine +import androidx.compose.ui.unit.dp +import dev.inmo.kmppscriptbuilder.core.models.* +import dev.inmo.kmppscriptbuilder.desktop.utils.* + +class MavenInfoView : VerticalView("Project information") { + private var projectNameProperty by mutableStateOf("") + private var projectDescriptionProperty by mutableStateOf("") + private var projectUrlProperty by mutableStateOf("") + private var projectVcsUrlProperty by mutableStateOf("") + private var gpgSignProperty by mutableStateOf(GpgSigning.Disabled) + private var publishToMavenCentralProperty by mutableStateOf(false) + private val developersView = DevelopersView() + private val repositoriesView = RepositoriesView() + + var mavenConfig: MavenConfig + get() = MavenConfig( + projectNameProperty.ifBlank { defaultProjectName }, + projectDescriptionProperty.ifBlank { defaultProjectDescription }, + projectUrlProperty, + projectVcsUrlProperty, + developersView.developers, + repositoriesView.repositories + if (publishToMavenCentralProperty) { + listOf(SonatypeRepository) + } else { + emptyList() + }, + gpgSignProperty + ) + set(value) { + projectNameProperty = value.name + projectDescriptionProperty = value.description + projectUrlProperty = value.url + projectVcsUrlProperty = value.vcsUrl + gpgSignProperty = if (value.includeGpgSigning) { + GpgSigning.Enabled + } else { + value.gpgSigning + } + publishToMavenCentralProperty = value.repositories.any { it == SonatypeRepository } + developersView.developers = value.developers + repositoriesView.repositories = value.repositories.filter { it != SonatypeRepository } +// developersView.developers = value.developers + } + + @Composable + private fun addGpgSigningButton(gpgSigning: GpgSigning) { + if (gpgSignProperty == gpgSigning) { + Button({}, Modifier.padding(8.dp)) { + Text(gpgSigning.name) + } + } else { + OutlinedButton( + { + gpgSignProperty = gpgSigning + }, + Modifier.padding(8.dp) + ) { + Text(gpgSigning.name) + } + } + } + + override val content: @Composable ColumnScope.() -> Unit = { + CommonTextField( + projectNameProperty, + "Public project name" + ) { projectNameProperty = it } + CommonTextField( + projectDescriptionProperty, + "Public project description" + ) { projectDescriptionProperty = it } + CommonTextField( + projectUrlProperty, + "Public project URL" + ) { projectUrlProperty = it } + CommonTextField( + projectVcsUrlProperty, + "Public project VCS URL (with .git)" + ) { projectVcsUrlProperty = it } + + Row(verticalAlignment = Alignment.CenterVertically) { + Text("Gpg Signing: ") + addGpgSigningButton(GpgSigning.Disabled) + addGpgSigningButton(GpgSigning.Optional) + addGpgSigningButton(GpgSigning.Enabled) + } + + SwitchWithLabel( + "Include publication to MavenCentral", + publishToMavenCentralProperty, + placeSwitchAtTheStart = true + ) { publishToMavenCentralProperty = it } + developersView.init() + repositoriesView.init() + } +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ProjectTypeView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ProjectTypeView.kt new file mode 100644 index 0000000..a35c2e9 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ProjectTypeView.kt @@ -0,0 +1,40 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import dev.inmo.kmppscriptbuilder.core.models.* +import dev.inmo.kmppscriptbuilder.desktop.utils.VerticalView + +class ProjectTypeView : VerticalView("Project type") { + var projectType by mutableStateOf(MultiplatformProjectType) + + @Composable + private fun addProjectTypeButton(newProjectType: ProjectType) { + if (projectType == newProjectType) { + Button({}, Modifier.padding(8.dp)) { + Text(newProjectType.name) + } + } else { + OutlinedButton( + { + projectType = newProjectType + }, + Modifier.padding(8.dp) + ) { + Text(newProjectType.name) + } + } + } + + override val content: @Composable ColumnScope.() -> Unit = { + Row(verticalAlignment = Alignment.CenterVertically) { + addProjectTypeButton(MultiplatformProjectType) + addProjectTypeButton(JVMProjectType) + addProjectTypeButton(JSProjectType) + } + } +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/RepositoriesView.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/RepositoriesView.kt new file mode 100644 index 0000000..e022ee7 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/RepositoriesView.kt @@ -0,0 +1,45 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.* +import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository +import dev.inmo.kmppscriptbuilder.desktop.utils.CommonTextField + +class RepositoryState( + name: String = "", + url: String = "" +) { + var name: String by mutableStateOf(name) + var url: String by mutableStateOf(url) + + fun toRepository() = MavenPublishingRepository(name, url) +} + +private fun MavenPublishingRepository.toRepositoryState() = RepositoryState(name, url) + +class RepositoriesView : ListView("Repositories info") { + var repositories: List + get() = itemsList.map { it.toRepository() } + set(value) { + itemsList.clear() + itemsList.addAll( + value.map { it.toRepositoryState() } + ) + } + + override val addItemText: String = "Add repository" + override val removeItemText: String = "Remove repository" + + override fun createItem(): RepositoryState = RepositoryState() + @Composable + override fun buildView(item: RepositoryState) { + CommonTextField( + item.name, + "Repository name" + ) { item.name = it } + CommonTextField( + item.url, + "Repository url" + ) { item.url = it } + } + +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt new file mode 100644 index 0000000..c2f5fbe --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt @@ -0,0 +1,16 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.Composable + +expect abstract class View() { + @Composable + abstract fun build() +} + +@Composable +expect fun View.DrawVertically(title: String, block: @Composable () -> Unit) + +@Composable +fun T.init(): T = apply { + build() +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/Drawer.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/Drawer.kt new file mode 100644 index 0000000..ea558aa --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/Drawer.kt @@ -0,0 +1,8 @@ +package dev.inmo.kmppscriptbuilder.core.ui.utils + +import androidx.compose.runtime.Composable + +interface Drawer { + @Composable + fun T.draw() +} diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/UIElements.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/UIElements.kt new file mode 100644 index 0000000..ff0fc83 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/UIElements.kt @@ -0,0 +1,21 @@ +package dev.inmo.kmppscriptbuilder.core.ui.utils + +import androidx.compose.runtime.Composable + +@Composable +expect fun TitleText(text: String) + +@Composable +expect fun CommonText(text: String) + +@Composable +expect fun CommonTextField(presetText: String, hint: String, onChange: (String) -> Unit) + +@Composable +expect fun SwitchWithLabel( + label: String, + checked: Boolean, + placeSwitchAtTheStart: Boolean = false, + switchEnabled: Boolean = true, + onCheckedChange: (Boolean) -> Unit +) diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/FilesHandling.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/FilesHandling.kt new file mode 100644 index 0000000..9ef1f19 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/FilesHandling.kt @@ -0,0 +1,23 @@ +package dev.inmo.kmppscriptbuilder.core.utils + +import dev.inmo.kmppscriptbuilder.core.models.Config +import dev.inmo.micro_utils.common.MPPFile + +internal const val appExtension = "kpsb" + +private var lastFile: MPPFile? = null + +fun loadConfigFile(file: MPPFile): Config { + lastFile = file + return serialFormat.decodeFromString(Config.serializer(), file.text()) +} + +expect fun MPPFile.text(): String + +expect fun loadConfig(): Config? + +expect fun saveConfig(config: Config): Boolean + +expect fun exportGradle(config: Config): Boolean + +expect fun saveAs(config: Config): Boolean diff --git a/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/OpenLink.kt b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/OpenLink.kt new file mode 100644 index 0000000..ab00379 --- /dev/null +++ b/core/src/commonMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/OpenLink.kt @@ -0,0 +1,3 @@ +package dev.inmo.kmppscriptbuilder.core.utils + +expect fun openLink(link: String): Boolean diff --git a/core/src/jsMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt b/core/src/jsMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt new file mode 100644 index 0000000..a7f4551 --- /dev/null +++ b/core/src/jsMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt @@ -0,0 +1,12 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.runtime.Composable + +actual abstract class View { + @Composable + actual abstract fun build() +} + +@Composable +actual fun View.DrawVertically(title: String, block: @Composable () -> Unit) { +} diff --git a/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/BuilderViewDrawer.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/BuilderViewDrawer.kt new file mode 100644 index 0000000..1e7f6bf --- /dev/null +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/BuilderViewDrawer.kt @@ -0,0 +1,65 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Divider +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText +import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer +import dev.inmo.kmppscriptbuilder.core.utils.exportGradle +import dev.inmo.kmppscriptbuilder.core.utils.loadConfig +import dev.inmo.kmppscriptbuilder.core.utils.saveAs +import dev.inmo.kmppscriptbuilder.core.utils.saveConfig + +actual object BuilderViewDrawer : Drawer { + override fun BuilderView.draw() { + Box(Modifier.fillMaxSize()) { + Column() { + TopAppBar( + @Composable { + Text("Kotlin publication scripts builder", Modifier.clickable { println(config) }) + }, + actions = { + createIcon("Open file", "images/open_file.svg") { + loadConfig()?.also { + config = it + } + } + + if (saveAvailableState) { + createIcon("Save", "images/save_file.svg") { + saveConfig(config) + } + } + + if (saveAvailableState) { + createIcon("Export Gradle script", "images/export_gradle.svg") { + exportGradle(config) + } + } + + createIcon("Save as", "images/save_as.svg") { + if (saveAs(config)) { + saveAvailableState = true + } + } + } + ) + Column(Modifier.padding(8.dp)) { + projectTypeView.init() + Divider() + licensesView.init() + Divider() + mavenInfoView.init() + } + } + } + } +} diff --git a/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ListViewDrawer.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ListViewDrawer.kt new file mode 100644 index 0000000..c62f426 --- /dev/null +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/ListViewDrawer.kt @@ -0,0 +1,25 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText +import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer + +actual class ListViewDrawer : Drawer> { + override fun ListView.draw() { + Button({ itemsList.add(createItem()) }) { + CommonText(addItemText) + } + itemsList.forEach { item -> + Column(Modifier.padding(8.dp)) { + buildView(item) + Button({ itemsList.remove(item) }, Modifier.padding(8.dp)) { + CommonText(removeItemText) + } + } + } + } +} diff --git a/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt new file mode 100644 index 0000000..8a93119 --- /dev/null +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/View.kt @@ -0,0 +1,31 @@ +package dev.inmo.kmppscriptbuilder.core.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import dev.inmo.kmppscriptbuilder.core.ui.utils.TitleText + +actual abstract class View { + internal open val defaultModifier = Modifier.fillMaxWidth().padding(8.dp) + @Composable + actual abstract fun build() +} + +@Composable +actual fun View.DrawVertically( + title: String, + block: @Composable () -> Unit +) { + TitleText(title) + + Column(defaultModifier) { + block() + } + + Spacer(Modifier.fillMaxWidth().height(8.dp)) +} diff --git a/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/ActualUIElements.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/ActualUIElements.kt new file mode 100644 index 0000000..e9bc427 --- /dev/null +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/ActualUIElements.kt @@ -0,0 +1,71 @@ +package dev.inmo.kmppscriptbuilder.core.ui.utils + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Switch +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + + +val commonTextFieldTextStyle = TextStyle( + fontSize = 12.sp +) + +@Composable +actual fun SwitchWithLabel( + label: String, + checked: Boolean, + placeSwitchAtTheStart: Boolean, + switchEnabled: Boolean, + onCheckedChange: (Boolean) -> Unit +) { + Row(Modifier.padding(0.dp, 8.dp), Arrangement.Start, Alignment.Top) { + val switchCreator = @Composable { + Switch(checked, onCheckedChange, Modifier.padding(8.dp, 0.dp), enabled = switchEnabled) + } + if (placeSwitchAtTheStart) { + switchCreator() + } + Box(Modifier.fillMaxWidth().align(Alignment.CenterVertically).clickable { }) { + CommonText(label) + } + if (!placeSwitchAtTheStart) { + switchCreator() + } + } +} + +@Composable +actual fun CommonTextField(presetText: String, hint: String, onChange: (String) -> Unit) { + OutlinedTextField( + presetText, + onChange, + Modifier.fillMaxWidth(), + singleLine = true, + label = { + CommonText(hint) + } + ) +} + +@Composable +actual fun CommonText(text: String) { + Text(text) +} + +@Composable +actual fun TitleText(text: String) { + Text( + text, Modifier.padding(0.dp, 8.dp), fontSize = 18.sp + ) +} diff --git a/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/NewFileFilterFactory.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/NewFileFilterFactory.kt similarity index 89% rename from desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/NewFileFilterFactory.kt rename to core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/NewFileFilterFactory.kt index 122d60c..d08d960 100644 --- a/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/NewFileFilterFactory.kt +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/ui/utils/NewFileFilterFactory.kt @@ -1,4 +1,4 @@ -package dev.inmo.kmppscriptbuilder.desktop.utils +package dev.inmo.kmppscriptbuilder.core.ui.utils import java.io.File import javax.swing.filechooser.FileFilter diff --git a/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/ActualFilesHandling.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/ActualFilesHandling.kt new file mode 100644 index 0000000..cb76d23 --- /dev/null +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/ActualFilesHandling.kt @@ -0,0 +1,80 @@ +package dev.inmo.kmppscriptbuilder.core.utils + +import dev.inmo.kmppscriptbuilder.core.models.Config +import dev.inmo.kmppscriptbuilder.core.ui.utils.FileFilter +import dev.inmo.kmppscriptbuilder.core.utils.serialFormat +import dev.inmo.micro_utils.common.MPPFile +import java.io.File +import javax.swing.JFileChooser + +private const val appExtension = "kpsb" + +private var lastFile: File? = null + +fun loadConfigFile(file: File): Config { + lastFile = file + return serialFormat.decodeFromString(Config.serializer(), file.readText()) +} + +actual fun MPPFile.text() = readText() + +actual fun loadConfig(): Config? { + val fc = JFileChooser(lastFile ?.parent) + fc.addChoosableFileFilter(FileFilter("Kotlin Publication Scripts Builder", Regex(".*\\.$appExtension"))) + fc.addChoosableFileFilter(FileFilter("JSON", Regex(".*\\.json"))) + return when (fc.showOpenDialog(null)) { + JFileChooser.APPROVE_OPTION -> { + val file = fc.selectedFile + lastFile = file + return serialFormat.decodeFromString(Config.serializer(), fc.selectedFile.readText()) + } + else -> null + } +} + +actual fun saveConfig(config: Config): Boolean { + return lastFile ?.also { + it.writeText(serialFormat.encodeToString(Config.serializer(), config)) + } != null +} + +actual fun exportGradle(config: Config): Boolean { + val fc = JFileChooser(lastFile ?.parent) + fc.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY + return when (fc.showSaveDialog(null)) { + JFileChooser.APPROVE_OPTION -> { + val file = fc.selectedFile + val mavenConfigContent = config.type.buildMavenGradleConfig( + config.mavenConfig, + config.licenses + ) + File(file, "publish.gradle").apply { + delete() + createNewFile() + writeText(mavenConfigContent) + } + true + } + else -> false + } +} + +actual fun saveAs(config: Config): Boolean { + val fc = JFileChooser(lastFile ?.parent) + fc.addChoosableFileFilter(FileFilter("Kotlin Publication Scripts Builder", Regex(".*\\.$appExtension"))) + fc.addChoosableFileFilter(FileFilter("JSON", Regex(".*\\.json"))) + return when (fc.showSaveDialog(null)) { + JFileChooser.APPROVE_OPTION -> { + val file = fc.selectedFile + val resultFile = if (file.extension == "") { + File(file.parentFile, "${file.name}.$appExtension") + } else { + file + } + resultFile.writeText(serialFormat.encodeToString(Config.serializer(), config)) + lastFile = resultFile + true + } + else -> false + } +} diff --git a/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/OpenLink.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/openLink.kt similarity index 80% rename from desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/OpenLink.kt rename to core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/openLink.kt index c3550ec..32551d5 100644 --- a/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/OpenLink.kt +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/openLink.kt @@ -1,9 +1,9 @@ -package dev.inmo.kmppscriptbuilder.desktop.utils +package dev.inmo.kmppscriptbuilder.core.utils import java.awt.Desktop import java.net.URI -fun openLink(link: String): Boolean { +actual fun openLink(link: String): Boolean { val desktop = if (Desktop.isDesktopSupported()) Desktop.getDesktop() else null if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { try { diff --git a/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/text.kt b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/text.kt new file mode 100644 index 0000000..2ca09b5 --- /dev/null +++ b/core/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/core/utils/text.kt @@ -0,0 +1,2 @@ +package dev.inmo.kmppscriptbuilder.core.utils + diff --git a/desktop/build.gradle b/desktop/build.gradle index 2190cdd..023d7f8 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -1,17 +1,13 @@ plugins { id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.plugin.serialization" - id("org.jetbrains.compose") version "$compose_version" + alias(libs.plugins.jb.compose) } apply from: "$mppJavaProjectPresetPath" kotlin { - jvm { - compilations.main.kotlinOptions { - jvmTarget = "11" - } - } + jvm() sourceSets { commonMain { dependencies { @@ -22,7 +18,7 @@ kotlin { jvmMain { dependencies { implementation(compose.desktop.currentOs) - api "io.ktor:ktor-client-cio:$ktor_version" + implementation libs.ktor.client.cio } } } diff --git a/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/FilesHandling.kt b/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/FilesHandling.kt index 7869aa7..6eee212 100644 --- a/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/FilesHandling.kt +++ b/desktop/src/jvmMain/kotlin/dev/inmo/kmppscriptbuilder/desktop/utils/FilesHandling.kt @@ -1,6 +1,7 @@ package dev.inmo.kmppscriptbuilder.desktop.utils import dev.inmo.kmppscriptbuilder.core.models.Config +import dev.inmo.kmppscriptbuilder.core.ui.utils.FileFilter import dev.inmo.kmppscriptbuilder.core.utils.serialFormat import java.io.File import javax.swing.JFileChooser diff --git a/gradle.properties b/gradle.properties index cce3b81..2fba966 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,14 +3,12 @@ org.gradle.parallel=true kotlin.js.generate.externals=true kotlin.incremental=true kotlin.incremental.js=true -android.useAndroidX=true -android.enableJetifier=true kotlin_version=1.7.20 kotlin_coroutines_version=1.6.4 kotlin_serialisation_core_version=1.4.1 ktor_version=2.1.3 -micro_utils_version=0.14.1 +micro_utils_version=0.14.2 compose_version=1.2.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..012165e --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,41 @@ +[versions] + +kt = "1.7.20" +kt-serialization = "1.4.1" +kt-coroutines = "1.6.4" + +jb-compose = "1.2.1" +jb-dokka = "1.7.20" +microutils = "0.14.2" + +ktor = "2.1.3" + +gh-release = "2.4.1" + +[libraries] + +kt-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kt" } + +kt-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kt-serialization" } + +kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" } + +ktor-client = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } +ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } + +microutils-common = { module = "dev.inmo:micro_utils.common", version.ref = "microutils" } +microutils-coroutines = { module = "dev.inmo:micro_utils.coroutines", version.ref = "microutils" } + + +kt-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref = "kt" } +kt-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kt" } + + +buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" } +buildscript-kt-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kt" } +buildscript-jb-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "jb-dokka" } +buildscript-gh-release = { module = "com.github.breadmoirai:github-release", version.ref = "gh-release" } + +[plugins] + +jb-compose = { id = "org.jetbrains.compose", version.ref = "jb-compose" }