diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 5383e2d02f..23d15b1d51 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -17,6 +17,7 @@
+
diff --git a/build.gradle.kts b/build.gradle.kts
index 72e638bdf0..de3d33c176 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -17,11 +17,10 @@ dependencies {
}
tasks {
- val mergeSarifReports by registering(MergeSarifTask::class) {
+ val mergeSarifReports by
+ registering(MergeSarifTask::class) {
source(configurations.outgoingSarif)
include { it.file.extension == "sarif" }
}
- register("check") {
- dependsOn(mergeSarifReports)
- }
+ register("check") { dependsOn(mergeSarifReports) }
}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 684c247eed..3e202f8bbb 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -5,11 +5,7 @@ plugins {
kotlin {
sourceSets {
- all {
- languageSettings {
- optIn("kotlinx.serialization.ExperimentalSerializationApi")
- }
- }
+ all { languageSettings { optIn("kotlinx.serialization.ExperimentalSerializationApi") } }
}
}
@@ -24,7 +20,8 @@ dependencies {
implementation(libs.kotlinx.serialization.json)
implementation(libs.poko.gradlePlugin)
- // Enables using type-safe accessors to reference plugins from the [plugins] block defined in version catalogs.
+ // Enables using type-safe accessors to reference plugins from the [plugins] block defined in
+ // version catalogs.
// Context: https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
}
diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts
index fa3d003c83..a14349429a 100644
--- a/buildSrc/settings.gradle.kts
+++ b/buildSrc/settings.gradle.kts
@@ -10,9 +10,5 @@ dependencyResolutionManagement {
gradlePluginPortal()
mavenCentral()
}
- versionCatalogs {
- create("libs") {
- from(files("../gradle/libs.versions.toml"))
- }
- }
+ versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } }
}
diff --git a/buildSrc/src/main/kotlin/IdeaConfiguration.kt b/buildSrc/src/main/kotlin/IdeaConfiguration.kt
index c9af48d9f7..45ee2b07c7 100644
--- a/buildSrc/src/main/kotlin/IdeaConfiguration.kt
+++ b/buildSrc/src/main/kotlin/IdeaConfiguration.kt
@@ -4,38 +4,43 @@ import org.gradle.api.provider.Property
import org.gradle.jvm.toolchain.JavaLanguageVersion
enum class SupportedIJVersion {
- IJ_232,
- IJ_233
+ IJ_232,
+ IJ_233
}
private var warned = AtomicBoolean(false)
fun Project.supportedIJVersion(): SupportedIJVersion {
- val prop = kotlin.runCatching {
- rootProject.findProperty("supported.ij.version")?.toString() ?:
- localProperty("supported.ij.version")
- }.getOrNull()
+ val prop =
+ kotlin
+ .runCatching {
+ rootProject.findProperty("supported.ij.version")?.toString()
+ ?: localProperty("supported.ij.version")
+ }
+ .getOrNull()
- if (prop == null) {
- if (!warned.getAndSet(true)) {
- logger.warn(
- """
+ if (prop == null) {
+ if (!warned.getAndSet(true)) {
+ logger.warn(
+ """
No 'supported.ij.version' property provided. Falling back to IJ 233.
It is recommended to provide it using local.properties file or -Psupported.ij.version to avoid unexpected behavior.
- """.trimIndent()
- )
- }
- return SupportedIJVersion.IJ_233
+ """
+ .trimIndent()
+ )
}
+ return SupportedIJVersion.IJ_233
+ }
- return when (prop) {
- "232" -> SupportedIJVersion.IJ_232
- "233" -> SupportedIJVersion.IJ_233
- else -> error(
- "Invalid 'supported.ij.version' with value '$prop' is provided. " +
- "It should be in set of these values: ('232', '233')"
- )
- }
+ return when (prop) {
+ "232" -> SupportedIJVersion.IJ_232
+ "233" -> SupportedIJVersion.IJ_233
+ else ->
+ error(
+ "Invalid 'supported.ij.version' with value '$prop' is provided. " +
+ "It should be in set of these values: ('232', '233')"
+ )
+ }
}
fun Property.assign(version: Int) =
diff --git a/buildSrc/src/main/kotlin/LocalProperties.kt b/buildSrc/src/main/kotlin/LocalProperties.kt
index 526aa08f36..4480a839f1 100644
--- a/buildSrc/src/main/kotlin/LocalProperties.kt
+++ b/buildSrc/src/main/kotlin/LocalProperties.kt
@@ -1,12 +1,12 @@
-import org.gradle.api.Project
import java.util.Properties
+import org.gradle.api.Project
internal fun Project.localProperty(propertyName: String): String? {
- val localPropertiesFile = rootProject.file("local.properties")
- if (!localPropertiesFile.exists()) {
- return null
- }
- val properties = Properties()
- localPropertiesFile.inputStream().use { properties.load(it) }
- return properties.getProperty(propertyName)
-}
\ No newline at end of file
+ val localPropertiesFile = rootProject.file("local.properties")
+ if (!localPropertiesFile.exists()) {
+ return null
+ }
+ val properties = Properties()
+ localPropertiesFile.inputStream().use { properties.load(it) }
+ return properties.getProperty(propertyName)
+}
diff --git a/buildSrc/src/main/kotlin/MergeSarifTask.kt b/buildSrc/src/main/kotlin/MergeSarifTask.kt
index d54a7c44d9..72abdfbf8c 100644
--- a/buildSrc/src/main/kotlin/MergeSarifTask.kt
+++ b/buildSrc/src/main/kotlin/MergeSarifTask.kt
@@ -12,39 +12,45 @@ import org.gradle.api.tasks.TaskAction
@CacheableTask
open class MergeSarifTask : SourceTask() {
- init {
- group = "verification"
- }
+ init {
+ group = "verification"
+ }
- @get:OutputFile
- val mergedSarifPath: RegularFileProperty = project.objects.fileProperty()
- .convention(project.layout.buildDirectory.file("reports/static-analysis.sarif"))
+ @get:OutputFile
+ val mergedSarifPath: RegularFileProperty =
+ project.objects
+ .fileProperty()
+ .convention(project.layout.buildDirectory.file("reports/static-analysis.sarif"))
- @TaskAction
- fun merge() {
- val json = Json { prettyPrint = true }
+ @TaskAction
+ fun merge() {
+ val json = Json { prettyPrint = true }
- logger.lifecycle("Merging ${source.files.size} SARIF file(s)...")
- logger.lifecycle(source.files.joinToString("\n") { " * ~${it.path.removePrefix(project.rootDir.path)}" })
+ logger.lifecycle("Merging ${source.files.size} SARIF file(s)...")
+ logger.lifecycle(
+ source.files.joinToString("\n") { " * ~${it.path.removePrefix(project.rootDir.path)}" }
+ )
- val merged = SarifSchema210(
- schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
- version = Version.The210,
- runs = source.files
- .asSequence()
- .filter { it.extension == "sarif" }
- .map { file -> file.inputStream().use { json.decodeFromStream(it) } }
- .flatMap { report -> report.runs }
- .groupBy { run -> run.tool.driver.guid ?: run.tool.driver.name }
- .values
- .asSequence()
- .filter { it.isNotEmpty() }
- .map { run -> run.first().copy(results = run.flatMap { it.results ?: emptyList() }) }
- .toList()
- )
- logger.lifecycle("Merged SARIF file contains ${merged.runs.size} run(s)")
- logger.info("Writing merged SARIF file to $mergedSarifPath...")
- mergedSarifPath.asFile.get().outputStream()
- .use { json.encodeToStream(merged, it) }
- }
+ val merged =
+ SarifSchema210(
+ schema =
+ "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+ version = Version.The210,
+ runs =
+ source.files
+ .asSequence()
+ .filter { it.extension == "sarif" }
+ .map { file -> file.inputStream().use { json.decodeFromStream(it) } }
+ .flatMap { report -> report.runs }
+ .groupBy { run -> run.tool.driver.guid ?: run.tool.driver.name }
+ .values
+ .asSequence()
+ .filter { it.isNotEmpty() }
+ .map { run -> run.first().copy(results = run.flatMap { it.results ?: emptyList() }) }
+ .toList()
+ )
+ logger.lifecycle("Merged SARIF file contains ${merged.runs.size} run(s)")
+ logger.info("Writing merged SARIF file to $mergedSarifPath...")
+ mergedSarifPath.asFile.get().outputStream().use { json.encodeToStream(merged, it) }
+ }
}
diff --git a/buildSrc/src/main/kotlin/PublishConfiguration.kt b/buildSrc/src/main/kotlin/PublishConfiguration.kt
index b2ea7d2f87..0a57d38874 100644
--- a/buildSrc/src/main/kotlin/PublishConfiguration.kt
+++ b/buildSrc/src/main/kotlin/PublishConfiguration.kt
@@ -1,4 +1,4 @@
-@file:Suppress("UnstableApiUsage")
+@file:Suppress("UnstableApiUsage", "UnusedImports")
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPom
@@ -32,4 +32,4 @@ internal fun MavenPom.configureJewelPom() {
developerConnection = "scm:git:https://github.com/JetBrains/jewel.git"
url = "https://github.com/JetBrains/jewel"
}
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/kotlin/ValidatePublicApiTask.kt b/buildSrc/src/main/kotlin/ValidatePublicApiTask.kt
index af80f3b7eb..56eeee1afc 100644
--- a/buildSrc/src/main/kotlin/ValidatePublicApiTask.kt
+++ b/buildSrc/src/main/kotlin/ValidatePublicApiTask.kt
@@ -1,100 +1,103 @@
+import java.io.File
+import java.util.Stack
import org.gradle.api.GradleException
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskAction
-import java.io.File
-import java.util.Stack
@CacheableTask
open class ValidatePublicApiTask : SourceTask() {
- init {
- group = "verification"
+ init {
+ group = "verification"
- // The output is never really used, it is here for cacheability reasons only
- outputs.file(project.layout.buildDirectory.file("apiValidationRun"))
- }
+ // The output is never really used, it is here for cacheability reasons only
+ outputs.file(project.layout.buildDirectory.file("apiValidationRun"))
+ }
- private val classFqnRegex = "public (?:\\w+ )*class (\\S+)\\b".toRegex()
+ private val classFqnRegex = "public (?:\\w+ )*class (\\S+)\\b".toRegex()
- @Suppress("ConvertToStringTemplate") // The odd concatenation is needed because of $; escapes get confused
- private val copyMethodRegex = ("public static synthetic fun copy(-\\w+)?" + "\\$" + "default\\b").toRegex()
+ @Suppress(
+ "ConvertToStringTemplate"
+ ) // The odd concatenation is needed because of $; escapes get confused
+ private val copyMethodRegex =
+ ("public static synthetic fun copy(-\\w+)?" + "\\$" + "default\\b").toRegex()
- @TaskAction
- fun validatePublicApi() {
- logger.info("Validating ${source.files.size} API file(s)...")
+ @TaskAction
+ fun validatePublicApi() {
+ logger.info("Validating ${source.files.size} API file(s)...")
- val violations = mutableMapOf>()
- inputs.files.forEach { apiFile ->
- logger.lifecycle("Validating public API from file ${apiFile.path}")
+ val violations = mutableMapOf>()
+ inputs.files.forEach { apiFile ->
+ logger.lifecycle("Validating public API from file ${apiFile.path}")
- apiFile.useLines { lines ->
- val actualDataClasses = findDataClasses(lines)
+ apiFile.useLines { lines ->
+ val actualDataClasses = findDataClasses(lines)
- if (actualDataClasses.isNotEmpty()) {
- violations[apiFile] = actualDataClasses
- }
- }
- }
-
- if (violations.isNotEmpty()) {
- val message = buildString {
- appendLine("Data classes found in public API.")
- appendLine()
-
- for ((file, dataClasses) in violations.entries) {
- appendLine("In file ${file.path}:")
- for (dataClass in dataClasses) {
- appendLine(" * ${dataClass.replace("/", ".")}")
- }
- appendLine()
- }
- }
-
- throw GradleException(message)
- } else {
- logger.lifecycle("No public API violations found.")
+ if (actualDataClasses.isNotEmpty()) {
+ violations[apiFile] = actualDataClasses
}
+ }
}
- private fun findDataClasses(lines: Sequence): Set {
- val currentClassStack = Stack()
- val dataClasses = mutableMapOf()
-
- for (line in lines) {
- if (line.isBlank()) continue
-
- val matchResult = classFqnRegex.find(line)
- if (matchResult != null) {
- val classFqn = matchResult.groupValues[1]
- currentClassStack.push(classFqn)
- continue
- }
-
- if (line.contains("}")) {
- currentClassStack.pop()
- continue
- }
-
- val fqn = currentClassStack.peek()
- if (copyMethodRegex.find(line) != null) {
- val info = dataClasses.getOrPut(fqn) { DataClassInfo(fqn) }
- info.hasCopyMethod = true
- } else if (line.contains("public static final synthetic fun box-impl")) {
- val info = dataClasses.getOrPut(fqn) { DataClassInfo(fqn) }
- info.isLikelyValueClass = true
- }
+ if (violations.isNotEmpty()) {
+ val message = buildString {
+ appendLine("Data classes found in public API.")
+ appendLine()
+
+ for ((file, dataClasses) in violations.entries) {
+ appendLine("In file ${file.path}:")
+ for (dataClass in dataClasses) {
+ appendLine(" * ${dataClass.replace("/", ".")}")
+ }
+ appendLine()
}
+ }
- val actualDataClasses = dataClasses.filterValues { it.hasCopyMethod && !it.isLikelyValueClass }
- .keys
- return actualDataClasses
+ throw GradleException(message)
+ } else {
+ logger.lifecycle("No public API violations found.")
}
+ }
+
+ private fun findDataClasses(lines: Sequence): Set {
+ val currentClassStack = Stack()
+ val dataClasses = mutableMapOf()
+
+ for (line in lines) {
+ if (line.isBlank()) continue
+
+ val matchResult = classFqnRegex.find(line)
+ if (matchResult != null) {
+ val classFqn = matchResult.groupValues[1]
+ currentClassStack.push(classFqn)
+ continue
+ }
+
+ if (line.contains("}")) {
+ currentClassStack.pop()
+ continue
+ }
+
+ val fqn = currentClassStack.peek()
+ if (copyMethodRegex.find(line) != null) {
+ val info = dataClasses.getOrPut(fqn) { DataClassInfo(fqn) }
+ info.hasCopyMethod = true
+ } else if (line.contains("public static final synthetic fun box-impl")) {
+ val info = dataClasses.getOrPut(fqn) { DataClassInfo(fqn) }
+ info.isLikelyValueClass = true
+ }
+ }
+
+ val actualDataClasses =
+ dataClasses.filterValues { it.hasCopyMethod && !it.isLikelyValueClass }.keys
+ return actualDataClasses
+ }
}
@Suppress("DataClassShouldBeImmutable") // Only used in a loop, saves memory and is faster
private data class DataClassInfo(
- val fqn: String,
- var hasCopyMethod: Boolean = false,
- var isLikelyValueClass: Boolean = false,
+ val fqn: String,
+ var hasCopyMethod: Boolean = false,
+ var isLikelyValueClass: Boolean = false,
)
diff --git a/buildSrc/src/main/kotlin/android-studio-releases-generator.gradle.kts b/buildSrc/src/main/kotlin/android-studio-releases-generator.gradle.kts
index 08e93233a6..ca9c1806bb 100644
--- a/buildSrc/src/main/kotlin/android-studio-releases-generator.gradle.kts
+++ b/buildSrc/src/main/kotlin/android-studio-releases-generator.gradle.kts
@@ -10,27 +10,26 @@ import org.jetbrains.kotlin.gradle.tasks.BaseKotlinCompile
val extension: StudioVersionsGenerationExtension =
extensions.findByType()
- ?: extensions.create("androidStudioReleasesGenerator", StudioVersionsGenerationExtension::class.java)
+ ?: extensions.create(
+ "androidStudioReleasesGenerator",
+ StudioVersionsGenerationExtension::class.java
+ )
val task =
tasks.register("generateAndroidStudioReleasesList") {
val className = ClassName.bestGuess(STUDIO_RELEASES_OUTPUT_CLASS_NAME)
- outputFile = extension.targetDir.file(
- className.packageName.replace(".", "/")
- .plus("/${className.simpleName}.kt")
- )
+ outputFile =
+ extension.targetDir.file(
+ className.packageName.replace(".", "/").plus("/${className.simpleName}.kt")
+ )
dataUrl = extension.dataUrl
resourcesDirs = extension.resourcesDirs
}
tasks {
- withType {
- dependsOn(task)
- }
+ withType { dependsOn(task) }
- withType {
- dependsOn(task)
- }
+ withType { dependsOn(task) }
}
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
diff --git a/buildSrc/src/main/kotlin/intellij-theme-generator.gradle.kts b/buildSrc/src/main/kotlin/intellij-theme-generator.gradle.kts
index 7c758d892d..593b7b6726 100644
--- a/buildSrc/src/main/kotlin/intellij-theme-generator.gradle.kts
+++ b/buildSrc/src/main/kotlin/intellij-theme-generator.gradle.kts
@@ -10,28 +10,28 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.tasks.BaseKotlinCompile
val extension = ThemeGeneratorContainer(container { ThemeGeneration(it, project) })
+
extensions.add("intelliJThemeGenerator", extension)
extension.all {
- val task = tasks.register("generate${GUtil.toCamelCase(name)}Theme") {
- outputFile = targetDir.file(this@all.themeClassName.map {
- val className = ClassName.bestGuess(it)
- className.packageName.replace(".", "/")
- .plus("/${className.simpleName}.kt")
- })
- themeClassName = this@all.themeClassName
- ideaVersion = this@all.ideaVersion
- themeFile = this@all.themeFile
- }
+ val task =
+ tasks.register("generate${GUtil.toCamelCase(name)}Theme") {
+ outputFile =
+ targetDir.file(
+ this@all.themeClassName.map {
+ val className = ClassName.bestGuess(it)
+ className.packageName.replace(".", "/").plus("/${className.simpleName}.kt")
+ }
+ )
+ themeClassName = this@all.themeClassName
+ ideaVersion = this@all.ideaVersion
+ themeFile = this@all.themeFile
+ }
tasks {
- withType {
- dependsOn(task)
- }
+ withType { dependsOn(task) }
- withType {
- dependsOn(task)
- }
+ withType { dependsOn(task) }
}
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
diff --git a/buildSrc/src/main/kotlin/jewel-check-public-api.gradle.kts b/buildSrc/src/main/kotlin/jewel-check-public-api.gradle.kts
index ac00fb5291..05f3b5e62a 100644
--- a/buildSrc/src/main/kotlin/jewel-check-public-api.gradle.kts
+++ b/buildSrc/src/main/kotlin/jewel-check-public-api.gradle.kts
@@ -14,18 +14,15 @@ apiValidation {
nonPublicMarkers.add("org.jetbrains.jewel.InternalJewelApi")
}
-poko {
- pokoAnnotation = "org.jetbrains.jewel.foundation.GenerateDataFunctions"
-}
+poko { pokoAnnotation = "org.jetbrains.jewel.foundation.GenerateDataFunctions" }
tasks {
- val validatePublicApi = register("validatePublicApi") {
- include { it.file.extension == "api" }
- source(project.fileTree("api"))
- dependsOn(named("apiCheck"))
- }
+ val validatePublicApi =
+ register("validatePublicApi") {
+ include { it.file.extension == "api" }
+ source(project.fileTree("api"))
+ dependsOn(named("apiCheck"))
+ }
- named("check") {
- dependsOn(validatePublicApi)
- }
+ named("check") { dependsOn(validatePublicApi) }
}
diff --git a/buildSrc/src/main/kotlin/jewel-ij-publish.gradle.kts b/buildSrc/src/main/kotlin/jewel-ij-publish.gradle.kts
index 1219a5a1b6..1d325cf1d2 100644
--- a/buildSrc/src/main/kotlin/jewel-ij-publish.gradle.kts
+++ b/buildSrc/src/main/kotlin/jewel-ij-publish.gradle.kts
@@ -9,13 +9,15 @@ plugins {
id("org.jetbrains.dokka")
}
-val sourcesJar by tasks.registering(Jar::class) {
+val sourcesJar by
+tasks.registering(Jar::class) {
from(kotlin.sourceSets.main.map { it.kotlin })
archiveClassifier = "sources"
destinationDirectory = layout.buildDirectory.dir("artifacts")
}
-val javadocJar by tasks.registering(Jar::class) {
+val javadocJar by
+tasks.registering(Jar::class) {
from(tasks.dokkaHtml)
archiveClassifier = "javadoc"
destinationDirectory = layout.buildDirectory.dir("artifacts")
@@ -29,15 +31,14 @@ publishing {
from(components["kotlin"])
artifact(javadocJar)
artifact(sourcesJar)
- val ijVersionRaw = when (supportedIJVersion()) {
- IJ_232 -> "232"
- IJ_233 -> "233"
- }
+ val ijVersionRaw =
+ when (supportedIJVersion()) {
+ IJ_232 -> "232"
+ IJ_233 -> "233"
+ }
version = project.version.toString().withVersionSuffix("ij-$ijVersionRaw")
artifactId = "jewel-${project.name}"
- pom {
- configureJewelPom()
- }
+ pom { configureJewelPom() }
}
}
}
@@ -45,8 +46,8 @@ publishing {
/**
* Adds suffix to the version taking SNAPSHOT suffix into account
*
- * For example, if [this] is "0.0.1-SNAPSHOT" and [suffix] is "ij-233"
- * then result will be "0.0.1-ij-233-SNAPSHOT"
+ * For example, if [this] is "0.0.1-SNAPSHOT" and [suffix] is "ij-233" then
+ * result will be "0.0.1-ij-233-SNAPSHOT"
*/
fun String.withVersionSuffix(suffix: String): String {
val splitString = this.split('-')
diff --git a/buildSrc/src/main/kotlin/jewel-linting.gradle.kts b/buildSrc/src/main/kotlin/jewel-linting.gradle.kts
index 6780820299..2a9e669034 100644
--- a/buildSrc/src/main/kotlin/jewel-linting.gradle.kts
+++ b/buildSrc/src/main/kotlin/jewel-linting.gradle.kts
@@ -6,15 +6,11 @@ plugins {
}
configurations {
- val dependencies = register("sarif") {
- isCanBeDeclared = true
- }
+ val dependencies = register("sarif") { isCanBeDeclared = true }
register("outgoingSarif") {
isCanBeConsumed = true
isCanBeResolved = true
extendsFrom(dependencies.get())
- attributes {
- attribute(Usage.USAGE_ATTRIBUTE, objects.named("sarif"))
- }
+ attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named("sarif")) }
}
}
diff --git a/buildSrc/src/main/kotlin/jewel-publish.gradle.kts b/buildSrc/src/main/kotlin/jewel-publish.gradle.kts
index b41f71b367..62824d862d 100644
--- a/buildSrc/src/main/kotlin/jewel-publish.gradle.kts
+++ b/buildSrc/src/main/kotlin/jewel-publish.gradle.kts
@@ -6,13 +6,15 @@ plugins {
id("org.jetbrains.dokka")
}
-val sourcesJar by tasks.registering(Jar::class) {
+val sourcesJar by
+tasks.registering(Jar::class) {
from(kotlin.sourceSets.main.map { it.kotlin })
archiveClassifier = "sources"
destinationDirectory = layout.buildDirectory.dir("artifacts")
}
-val javadocJar by tasks.registering(Jar::class) {
+val javadocJar by
+tasks.registering(Jar::class) {
from(tasks.dokkaHtml)
archiveClassifier = "javadoc"
destinationDirectory = layout.buildDirectory.dir("artifacts")
@@ -28,9 +30,7 @@ publishing {
artifact(sourcesJar)
version = project.version.toString()
artifactId = "jewel-${project.name}"
- pom {
- configureJewelPom()
- }
+ pom { configureJewelPom() }
}
}
}
diff --git a/buildSrc/src/main/kotlin/jewel.gradle.kts b/buildSrc/src/main/kotlin/jewel.gradle.kts
index 5e0b20a61b..8db0eb7426 100644
--- a/buildSrc/src/main/kotlin/jewel.gradle.kts
+++ b/buildSrc/src/main/kotlin/jewel.gradle.kts
@@ -6,14 +6,15 @@ plugins {
group = "org.jetbrains.jewel"
val gitHubRef: String? = System.getenv("GITHUB_REF")
-version = when {
- gitHubRef?.startsWith("refs/tags/") == true -> {
- gitHubRef.substringAfter("refs/tags/")
- .removePrefix("v")
- }
- else -> "1.0.0-SNAPSHOT"
-}
+version =
+ when {
+ gitHubRef?.startsWith("refs/tags/") == true -> {
+ gitHubRef.substringAfter("refs/tags/").removePrefix("v")
+ }
+
+ else -> "1.0.0-SNAPSHOT"
+ }
java {
toolchain {
@@ -31,11 +32,7 @@ kotlin {
explicitApi()
target {
- compilations.all {
- kotlinOptions {
- freeCompilerArgs += "-Xcontext-receivers"
- }
- }
+ compilations.all { kotlinOptions { freeCompilerArgs += "-Xcontext-receivers" } }
sourceSets.all {
languageSettings {
optIn("androidx.compose.foundation.ExperimentalFoundationApi")
@@ -54,8 +51,8 @@ detekt {
buildUponDefaultConfig = true
}
-val sarifReport: Provider = layout.buildDirectory
- .file("reports/ktlint-${project.name}.sarif")
+val sarifReport: Provider =
+ layout.buildDirectory.file("reports/ktlint-${project.name}.sarif")
tasks {
detektMain {
@@ -67,9 +64,7 @@ tasks {
}
}
- formatKotlinMain {
- exclude { it.file.absolutePath.contains("build/generated") }
- }
+ formatKotlinMain { exclude { it.file.absolutePath.contains("build/generated") } }
lintKotlinMain {
exclude { it.file.absolutePath.contains("build/generated") }
@@ -86,20 +81,18 @@ tasks {
configurations.named("sarif") {
outgoing {
- artifact(tasks.detektMain.flatMap { it.sarifReportFile }) {
- builtBy(tasks.detektMain)
- }
- artifact(sarifReport) {
- builtBy(tasks.lintKotlinMain)
- }
+ artifact(tasks.detektMain.flatMap { it.sarifReportFile }) { builtBy(tasks.detektMain) }
+ artifact(sarifReport) { builtBy(tasks.lintKotlinMain) }
}
}
fun Task.removeAssembleDependency() {
- setDependsOn(dependsOn.filter {
- when {
- it is Task && it.name == "assemble" -> false
- else -> true
+ setDependsOn(
+ dependsOn.filter {
+ when {
+ it is Task && it.name == "assemble" -> false
+ else -> true
+ }
}
- })
+ )
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleases.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleases.kt
index 3116ca3da7..8bfe56d118 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleases.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleases.kt
@@ -3,6 +3,8 @@ package org.jetbrains.jewel.buildlogic.demodata
import com.squareup.kotlinpoet.ClassName
import gradle.kotlin.dsl.accessors._c011fd04eb69b06af6f445fec200c5f6.main
import gradle.kotlin.dsl.accessors._c011fd04eb69b06af6f445fec200c5f6.sourceSets
+import java.io.File
+import java.net.URL
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.gradle.api.DefaultTask
@@ -16,60 +18,59 @@ import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
import org.gradle.kotlin.dsl.setProperty
-import java.io.File
-import java.net.URL
open class StudioVersionsGenerationExtension(project: Project) {
- val targetDir: DirectoryProperty = project.objects.directoryProperty()
- .convention(project.layout.buildDirectory.dir("generated/studio-releases/"))
+ val targetDir: DirectoryProperty =
+ project.objects
+ .directoryProperty()
+ .convention(project.layout.buildDirectory.dir("generated/studio-releases/"))
- val resourcesDirs: SetProperty = project.objects.setProperty()
- .convention(project.sourceSets.main.get().resources.srcDirs)
+ val resourcesDirs: SetProperty =
+ project.objects.setProperty().convention(project.sourceSets.main.get().resources.srcDirs)
- val dataUrl: Property = project.objects.property()
- .convention("https://jb.gg/android-studio-releases-list.json")
+ val dataUrl: Property =
+ project.objects.property().convention("https://jb.gg/android-studio-releases-list.json")
}
-internal const val STUDIO_RELEASES_OUTPUT_CLASS_NAME = "org.jetbrains.jewel.samples.ideplugin.releasessample.AndroidStudioReleases"
+internal const val STUDIO_RELEASES_OUTPUT_CLASS_NAME =
+ "org.jetbrains.jewel.samples.ideplugin.releasessample.AndroidStudioReleases"
open class AndroidStudioReleasesGeneratorTask : DefaultTask() {
- @get:OutputFile
- val outputFile: RegularFileProperty = project.objects.fileProperty()
+ @get:OutputFile val outputFile: RegularFileProperty = project.objects.fileProperty()
- @get:Input
- val dataUrl = project.objects.property()
+ @get:Input val dataUrl = project.objects.property()
- @get:Input
- val resourcesDirs = project.objects.setProperty()
+ @get:Input val resourcesDirs = project.objects.setProperty()
- init {
- group = "jewel"
- }
+ init {
+ group = "jewel"
+ }
- @TaskAction
- fun generate() {
- val json = Json {
- ignoreUnknownKeys = true
- isLenient = true
- }
- val url = dataUrl.get()
- val lookupDirs = resourcesDirs.get()
+ @TaskAction
+ fun generate() {
+ val json = Json {
+ ignoreUnknownKeys = true
+ isLenient = true
+ }
+ val url = dataUrl.get()
+ val lookupDirs = resourcesDirs.get()
- logger.lifecycle("Fetching Android Studio releases list from $url...")
- logger.debug(
- "Registered resources directories:\n" +
- lookupDirs.joinToString("\n") { " * ${it.absolutePath}" }
- )
- val releases = URL(url).openStream()
- .use { json.decodeFromStream(it) }
+ logger.lifecycle("Fetching Android Studio releases list from $url...")
+ logger.debug(
+ "Registered resources directories:\n" +
+ lookupDirs.joinToString("\n") { " * ${it.absolutePath}" }
+ )
+ val releases = URL(url).openStream().use { json.decodeFromStream(it) }
- val className = ClassName.bestGuess(STUDIO_RELEASES_OUTPUT_CLASS_NAME)
- val file = AndroidStudioReleasesReader.readFrom(releases, className, url, lookupDirs)
+ val className = ClassName.bestGuess(STUDIO_RELEASES_OUTPUT_CLASS_NAME)
+ val file = AndroidStudioReleasesReader.readFrom(releases, className, url, lookupDirs)
- val outputFile = outputFile.get().asFile
- outputFile.bufferedWriter().use { file.writeTo(it) }
- logger.lifecycle("Android Studio releases from $url parsed and code generated into ${outputFile.path}")
- }
+ val outputFile = outputFile.get().asFile
+ outputFile.bufferedWriter().use { file.writeTo(it) }
+ logger.lifecycle(
+ "Android Studio releases from $url parsed and code generated into ${outputFile.path}"
+ )
+ }
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleasesReader.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleasesReader.kt
index 77fdc4bed7..42f34ebd90 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleasesReader.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/AndroidStudioReleasesReader.kt
@@ -13,143 +13,162 @@ import java.io.File
import java.time.ZonedDateTime
private val ContentItemClassName =
- ClassName.bestGuess("org.jetbrains.jewel.samples.ideplugin.releasessample.ContentItem.AndroidStudio")
+ ClassName.bestGuess(
+ "org.jetbrains.jewel.samples.ideplugin.releasessample.ContentItem.AndroidStudio"
+ )
internal object AndroidStudioReleasesReader {
- fun readFrom(
- releases: ApiAndroidStudioReleases,
- className: ClassName,
- url: String,
- resourceDirs: Set,
- ) =
- FileSpec.builder(className).apply {
- indent(" ")
- addFileComment("Generated by the Jewel Android Studio Releases Generator\n")
- addFileComment("Generated from $url on ${ZonedDateTime.now()}\n")
-
- addImport("org.jetbrains.jewel.samples.ideplugin.releasessample", "ContentItem.AndroidStudio")
- addImport("kotlinx.datetime", "LocalDate")
-
- addType(
- TypeSpec.objectBuilder(className)
- .superclass(
- ClassName.bestGuess("org.jetbrains.jewel.samples.ideplugin.releasessample.ContentSource")
- .parameterizedBy(ContentItemClassName)
- )
- .apply {
- addProperty(
- PropertySpec.builder(
- name = "items",
- type = List::class.asClassName().parameterizedBy(ContentItemClassName),
- KModifier.OVERRIDE
- )
- .initializer(readReleases(releases, resourceDirs))
- .build()
- )
-
- addProperty(
- PropertySpec.builder(
- "displayName",
- type = String::class.asClassName(),
- KModifier.OVERRIDE
- )
- .initializer("\"%L\"", "Android Studio releases")
- .build()
- )
- }.build()
+ fun readFrom(
+ releases: ApiAndroidStudioReleases,
+ className: ClassName,
+ url: String,
+ resourceDirs: Set,
+ ) =
+ FileSpec.builder(className)
+ .apply {
+ indent(" ")
+ addFileComment("Generated by the Jewel Android Studio Releases Generator\n")
+ addFileComment("Generated from $url on ${ZonedDateTime.now()}\n")
+
+ addImport(
+ "org.jetbrains.jewel.samples.ideplugin.releasessample",
+ "ContentItem.AndroidStudio"
+ )
+ addImport("kotlinx.datetime", "LocalDate")
+
+ addType(
+ TypeSpec.objectBuilder(className)
+ .superclass(
+ ClassName.bestGuess(
+ "org.jetbrains.jewel.samples.ideplugin.releasessample.ContentSource"
+ )
+ .parameterizedBy(ContentItemClassName)
)
- }.build()
-
- private fun readReleases(releases: ApiAndroidStudioReleases, resourceDirs: Set) =
- releases.content.item
- .map { readRelease(it, resourceDirs) }
- .joinToCode(prefix = "listOf(\n", separator = ",\n", suffix = ")")
-
- private fun readRelease(release: ApiAndroidStudioReleases.Content.Item, resourceDirs: Set) =
- CodeBlock.builder()
.apply {
- add("AndroidStudio(\n")
- add(" displayText = \"%L\",\n", release.name)
- add(" imagePath = %L,\n", imagePathForOrNull(release, resourceDirs))
- add(" versionName = \"%L\",\n", release.version)
- add(" build = \"%L\",\n", release.build)
- add(" platformBuild = \"%L\",\n", release.platformBuild)
- add(" platformVersion = \"%L\",\n", release.platformVersion)
- add(" channel = %L,\n", readChannel(release.channel))
- add(" releaseDate = LocalDate(%L),\n", translateDate(release.date))
- add(" key = \"%L\",\n", release.build)
- add(")")
+ addProperty(
+ PropertySpec.builder(
+ name = "items",
+ type = List::class.asClassName().parameterizedBy(ContentItemClassName),
+ KModifier.OVERRIDE
+ )
+ .initializer(readReleases(releases, resourceDirs))
+ .build()
+ )
+
+ addProperty(
+ PropertySpec.builder(
+ "displayName",
+ type = String::class.asClassName(),
+ KModifier.OVERRIDE
+ )
+ .initializer("\"%L\"", "Android Studio releases")
+ .build()
+ )
}
.build()
-
- private fun imagePathForOrNull(release: ApiAndroidStudioReleases.Content.Item, resourceDirs: Set): String? {
- // Take the release animal from the name, remove spaces and voila'
- val releaseAnimal = release.name
- .substringBefore(" | ")
- .substringAfter("Android Studio")
- .trim()
- .replace(" ", "")
-
- if (releaseAnimal.isEmpty() || releaseAnimal.any { it.isDigit() }) return null
-
- // We only have stable and canary splash screens. Betas use the stable ones.
- val channel = release.channel.lowercase()
- .let {
- when (it) {
- "release", "rc", "stable", "beta", "patch" -> "stable"
- "canary", "preview", "alpha" -> "canary"
- else -> {
- println(" Note: channel '${it}' isn't supported for splash screens")
- null
- }
- }
- } ?: return null
-
- val splashPath = "/studio-splash-screens/$releaseAnimal-$channel.png"
- val splashFiles = resourceDirs.map { dir -> File(dir, splashPath) }
- if (splashFiles.none { it.isFile }) {
- println(" Note: expected splash screen file doesn't exist: '${splashPath}'")
- return null
+ )
+ }
+ .build()
+
+ private fun readReleases(releases: ApiAndroidStudioReleases, resourceDirs: Set) =
+ releases.content.item
+ .map { readRelease(it, resourceDirs) }
+ .joinToCode(prefix = "listOf(\n", separator = ",\n", suffix = ")")
+
+ private fun readRelease(release: ApiAndroidStudioReleases.Content.Item, resourceDirs: Set) =
+ CodeBlock.builder()
+ .apply {
+ add("AndroidStudio(\n")
+ add(" displayText = \"%L\",\n", release.name)
+ add(" imagePath = %L,\n", imagePathForOrNull(release, resourceDirs))
+ add(" versionName = \"%L\",\n", release.version)
+ add(" build = \"%L\",\n", release.build)
+ add(" platformBuild = \"%L\",\n", release.platformBuild)
+ add(" platformVersion = \"%L\",\n", release.platformVersion)
+ add(" channel = %L,\n", readChannel(release.channel))
+ add(" releaseDate = LocalDate(%L),\n", translateDate(release.date))
+ add(" key = \"%L\",\n", release.build)
+ add(")")
+ }
+ .build()
+
+ private fun imagePathForOrNull(
+ release: ApiAndroidStudioReleases.Content.Item,
+ resourceDirs: Set
+ ): String? {
+ // Take the release animal from the name, remove spaces and voila'
+ val releaseAnimal =
+ release.name.substringBefore(" | ").substringAfter("Android Studio").trim().replace(" ", "")
+
+ if (releaseAnimal.isEmpty() || releaseAnimal.any { it.isDigit() }) return null
+
+ // We only have stable and canary splash screens. Betas use the stable ones.
+ val channel =
+ release.channel.lowercase().let {
+ when (it) {
+ "release",
+ "rc",
+ "stable",
+ "beta",
+ "patch" -> "stable"
+ "canary",
+ "preview",
+ "alpha" -> "canary"
+ else -> {
+ println(" Note: channel '${it}' isn't supported for splash screens")
+ null
+ }
}
+ } ?: return null
- return "\"$splashPath\""
+ val splashPath = "/studio-splash-screens/$releaseAnimal-$channel.png"
+ val splashFiles = resourceDirs.map { dir -> File(dir, splashPath) }
+ if (splashFiles.none { it.isFile }) {
+ println(" Note: expected splash screen file doesn't exist: '${splashPath}'")
+ return null
}
- // This is the laziest crap ever, I am sorry.
- private fun translateDate(rawDate: String): String {
- val month = rawDate.substringBefore(" ").trimStart('0')
- val year = rawDate.substringAfterLast(" ".trimStart('0'))
- val day = rawDate.substring(month.length + 1, rawDate.length - year.length - 1).trimStart('0')
-
- if (day.isEmpty()) {
- println("$rawDate\nMonth: '$month'\nYear: '$year'")
- }
+ return "\"$splashPath\""
+ }
- val monthNumber = when (month.trim().lowercase()) {
- "january" -> 1
- "february" -> 2
- "march" -> 3
- "april" -> 4
- "may" -> 5
- "june" -> 6
- "july" -> 7
- "august" -> 8
- "september" -> 9
- "october" -> 10
- "november" -> 11
- "december" -> 12
- else -> error("Unrecognized month: $month")
- }
+ // This is the laziest crap ever, I am sorry.
+ private fun translateDate(rawDate: String): String {
+ val month = rawDate.substringBefore(" ").trimStart('0')
+ val year = rawDate.substringAfterLast(" ".trimStart('0'))
+ val day = rawDate.substring(month.length + 1, rawDate.length - year.length - 1).trimStart('0')
- return "$year, $monthNumber, $day"
+ if (day.isEmpty()) {
+ println("$rawDate\nMonth: '$month'\nYear: '$year'")
}
- private fun readChannel(rawChannel: String) =
- when (rawChannel.lowercase().trim()) {
- "stable", "patch", "release" -> "ReleaseChannel.Stable"
- "beta" -> "ReleaseChannel.Beta"
- "canary" -> "ReleaseChannel.Canary"
- else -> "ReleaseChannel.Other"
- }
+ val monthNumber =
+ when (month.trim().lowercase()) {
+ "january" -> 1
+ "february" -> 2
+ "march" -> 3
+ "april" -> 4
+ "may" -> 5
+ "june" -> 6
+ "july" -> 7
+ "august" -> 8
+ "september" -> 9
+ "october" -> 10
+ "november" -> 11
+ "december" -> 12
+ else -> error("Unrecognized month: $month")
+ }
+
+ return "$year, $monthNumber, $day"
+ }
+
+ private fun readChannel(rawChannel: String) =
+ when (rawChannel.lowercase().trim()) {
+ "stable",
+ "patch",
+ "release" -> "ReleaseChannel.Stable"
+ "beta" -> "ReleaseChannel.Beta"
+ "canary" -> "ReleaseChannel.Canary"
+ else -> "ReleaseChannel.Other"
+ }
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/ApiAndroidStudioReleases.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/ApiAndroidStudioReleases.kt
index 9fc61f19bf..24b96bab3a 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/ApiAndroidStudioReleases.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/demodata/ApiAndroidStudioReleases.kt
@@ -5,47 +5,33 @@ import kotlinx.serialization.Serializable
@Serializable
internal data class ApiAndroidStudioReleases(
- @SerialName("content")
- val content: Content = Content()
+ @SerialName("content") val content: Content = Content()
) {
+ @Serializable
+ internal data class Content(
+ @SerialName("item") val item: List- = listOf(),
+ @SerialName("version") val version: Int = 0
+ ) {
+
@Serializable
- internal data class Content(
- @SerialName("item")
- val item: List
- = listOf(),
- @SerialName("version")
- val version: Int = 0
+ internal data class Item(
+ @SerialName("build") val build: String,
+ @SerialName("channel") val channel: String,
+ @SerialName("date") val date: String,
+ @SerialName("download") val download: List = listOf(),
+ @SerialName("name") val name: String,
+ @SerialName("platformBuild") val platformBuild: String,
+ @SerialName("platformVersion") val platformVersion: String,
+ @SerialName("version") val version: String
) {
- @Serializable
- internal data class Item(
- @SerialName("build")
- val build: String,
- @SerialName("channel")
- val channel: String,
- @SerialName("date")
- val date: String,
- @SerialName("download")
- val download: List = listOf(),
- @SerialName("name")
- val name: String,
- @SerialName("platformBuild")
- val platformBuild: String,
- @SerialName("platformVersion")
- val platformVersion: String,
- @SerialName("version")
- val version: String
- ) {
-
- @Serializable
- internal data class Download(
- @SerialName("checksum")
- val checksum: String,
- @SerialName("link")
- val link: String,
- @SerialName("size")
- val size: String
- )
- }
+ @Serializable
+ internal data class Download(
+ @SerialName("checksum") val checksum: String,
+ @SerialName("link") val link: String,
+ @SerialName("size") val size: String
+ )
}
+ }
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/ApiIdeaReleases.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/ApiIdeaReleases.kt
index 12c6704a14..e9b83200b0 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/ApiIdeaReleases.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/ApiIdeaReleases.kt
@@ -5,14 +5,14 @@ import kotlinx.serialization.Serializable
@Serializable
internal data class ApiIdeaReleasesItem(
- @SerialName("code") val code: String,
- @SerialName("releases") val releases: List,
+ @SerialName("code") val code: String,
+ @SerialName("releases") val releases: List,
) {
- @Serializable
- internal data class Release(
- @SerialName("build") val build: String,
- @SerialName("type") val type: String,
- @SerialName("version") val version: String,
- )
+ @Serializable
+ internal data class Release(
+ @SerialName("build") val build: String,
+ @SerialName("type") val type: String,
+ @SerialName("version") val version: String,
+ )
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/CheckIdeaVersionTask.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/CheckIdeaVersionTask.kt
index 9a9a369acd..c031c68c49 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/CheckIdeaVersionTask.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/ideversion/CheckIdeaVersionTask.kt
@@ -1,160 +1,175 @@
package org.jetbrains.jewel.buildlogic.ideversion
import SupportedIJVersion
+import java.io.IOException
+import java.net.URL
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.TaskAction
import supportedIJVersion
-import java.io.IOException
-import java.net.URL
open class CheckIdeaVersionTask : DefaultTask() {
- private val releasesUrl =
- "https://data.services.jetbrains.com/products?" +
- "fields=code,releases,releases.version,releases.build,releases.type&" +
- "code=IC"
+ private val releasesUrl =
+ "https://data.services.jetbrains.com/products?" +
+ "fields=code,releases,releases.version,releases.build,releases.type&" +
+ "code=IC"
- private val versionRegex = "2\\d{2}\\.\\d+\\.\\d+(?:-EAP-SNAPSHOT)?".toRegex(RegexOption.IGNORE_CASE)
+ private val versionRegex =
+ "2\\d{2}\\.\\d+\\.\\d+(?:-EAP-SNAPSHOT)?".toRegex(RegexOption.IGNORE_CASE)
- init {
- group = "jewel"
+ init {
+ group = "jewel"
- val currentPlatformVersion = project.supportedIJVersion()
- enabled = project.name.endsWith(getPlatformSuffix(currentPlatformVersion))
- }
-
- private fun getPlatformSuffix(currentPlatformVersion: SupportedIJVersion) =
- when (currentPlatformVersion) {
- SupportedIJVersion.IJ_232 -> "232"
- SupportedIJVersion.IJ_233 -> "233"
- }
-
- @TaskAction
- fun generate() {
- val json = Json {
- ignoreUnknownKeys = true
- isLenient = true
- }
+ val currentPlatformVersion = project.supportedIJVersion()
+ enabled = project.name.endsWith(getPlatformSuffix(currentPlatformVersion))
+ }
- logger.lifecycle("Fetching IntelliJ Platform releases from $releasesUrl...")
- val icReleases =
- try {
- URL(releasesUrl).openStream()
- .use { json.decodeFromStream
>(it) }
- .first()
- } catch (e: IOException) {
- logger.warn(
- "Couldn't fetch IJ Platform releases, can't check for updates.\n" +
- "Cause: ${e::class.qualifiedName} — ${e.message}"
- )
- return
- } catch (e: RuntimeException) {
- logger.error("Unexpected error while fetching IJ Platform releases, can't check for updates.", e)
- return
- }
-
- check(icReleases.code == "IIC") { "Was expecting code IIC but was ${icReleases.code}" }
- check(icReleases.releases.isNotEmpty()) { "Was expecting to have releases but the list is empty" }
-
- val currentPlatformVersion = project.supportedIJVersion()
- val majorPlatformVersion = getRawPlatformVersion(currentPlatformVersion)
- val rawPlatformBuild = readPlatformBuild(currentPlatformVersion)
-
- val isCurrentBuildStable = !rawPlatformBuild.contains("EAP")
- val latestAvailableBuild = icReleases.releases.asSequence()
- .filter { it.version.startsWith(majorPlatformVersion) }
- .filter { if (isCurrentBuildStable) it.type == "release" else true }
- .sortedWith(ReleaseComparator)
- .last()
- logger.info("The latest IntelliJ Platform $majorPlatformVersion build is ${latestAvailableBuild.build}")
-
- val currentPlatformBuild = rawPlatformBuild.substringBefore('-')
- if (VersionComparator.compare(currentPlatformBuild, latestAvailableBuild.build) < 0) {
- throw GradleException(
- buildString {
- appendLine("IntelliJ Platform version dependency is out of date.")
- appendLine()
- appendLine("Current build: $rawPlatformBuild")
- append("Latest build: ${latestAvailableBuild.build}")
- if (!isCurrentBuildStable) append("-EAP-SNAPSHOT")
- appendLine()
- append("Detected channel: ")
- appendLine(if (isCurrentBuildStable) "stable" else "non-stable (eap/beta/rc)")
- }
- )
- }
- logger.lifecycle("No IntelliJ Platform version updates available. Current: $currentPlatformBuild")
+ private fun getPlatformSuffix(currentPlatformVersion: SupportedIJVersion) =
+ when (currentPlatformVersion) {
+ SupportedIJVersion.IJ_232 -> "232"
+ SupportedIJVersion.IJ_233 -> "233"
}
- private fun getRawPlatformVersion(currentPlatformVersion: SupportedIJVersion) = when (currentPlatformVersion) {
- SupportedIJVersion.IJ_232 -> "2023.2"
- SupportedIJVersion.IJ_233 -> "2023.3"
+ @TaskAction
+ fun generate() {
+ val json = Json {
+ ignoreUnknownKeys = true
+ isLenient = true
}
- private fun readPlatformBuild(platformVersion: SupportedIJVersion): String {
- val catalogFile = project.rootProject.file("gradle/libs.versions.toml")
- val dependencyName = when (platformVersion) {
- SupportedIJVersion.IJ_232 -> "idea232"
- SupportedIJVersion.IJ_233 -> "idea233"
- }
+ logger.lifecycle("Fetching IntelliJ Platform releases from $releasesUrl...")
+ val icReleases =
+ try {
+ URL(releasesUrl)
+ .openStream()
+ .use { json.decodeFromStream>(it) }
+ .first()
+ } catch (e: IOException) {
+ logger.warn(
+ "Couldn't fetch IJ Platform releases, can't check for updates.\n" +
+ "Cause: ${e::class.qualifiedName} — ${e.message}"
+ )
+ return
+ } catch (e: RuntimeException) {
+ logger.error(
+ "Unexpected error while fetching IJ Platform releases, can't check for updates.",
+ e
+ )
+ return
+ }
+
+ check(icReleases.code == "IIC") { "Was expecting code IIC but was ${icReleases.code}" }
+ check(icReleases.releases.isNotEmpty()) {
+ "Was expecting to have releases but the list is empty"
+ }
- val catalogDependencyLine = catalogFile
- .useLines { lines ->
- lines.find { it.startsWith(dependencyName) }
- }
- ?: throw GradleException(
- "Unable to find IJP dependency '$dependencyName' in the catalog file '${catalogFile.path}'"
- )
-
- val dependencyVersion = catalogDependencyLine.substringAfter(dependencyName)
- .trimStart(' ', '=')
- .trimEnd()
- .trim('"')
-
- if (!dependencyVersion.matches(versionRegex)) {
- throw GradleException("Invalid IJP version found in version catalog: '$dependencyVersion'")
+ val currentPlatformVersion = project.supportedIJVersion()
+ val majorPlatformVersion = getRawPlatformVersion(currentPlatformVersion)
+ val rawPlatformBuild = readPlatformBuild(currentPlatformVersion)
+
+ val isCurrentBuildStable = !rawPlatformBuild.contains("EAP")
+ val latestAvailableBuild =
+ icReleases.releases
+ .asSequence()
+ .filter { it.version.startsWith(majorPlatformVersion) }
+ .filter { if (isCurrentBuildStable) it.type == "release" else true }
+ .sortedWith(ReleaseComparator)
+ .last()
+ logger.info(
+ "The latest IntelliJ Platform $majorPlatformVersion build is ${latestAvailableBuild.build}"
+ )
+
+ val currentPlatformBuild = rawPlatformBuild.substringBefore('-')
+ if (VersionComparator.compare(currentPlatformBuild, latestAvailableBuild.build) < 0) {
+ throw GradleException(
+ buildString {
+ appendLine("IntelliJ Platform version dependency is out of date.")
+ appendLine()
+ appendLine("Current build: $rawPlatformBuild")
+ append("Latest build: ${latestAvailableBuild.build}")
+ if (!isCurrentBuildStable) append("-EAP-SNAPSHOT")
+ appendLine()
+ append("Detected channel: ")
+ appendLine(if (isCurrentBuildStable) "stable" else "non-stable (eap/beta/rc)")
}
+ )
+ }
+ logger.lifecycle(
+ "No IntelliJ Platform version updates available. Current: $currentPlatformBuild"
+ )
+ }
+
+ private fun getRawPlatformVersion(currentPlatformVersion: SupportedIJVersion) =
+ when (currentPlatformVersion) {
+ SupportedIJVersion.IJ_232 -> "2023.2"
+ SupportedIJVersion.IJ_233 -> "2023.3"
+ }
- return dependencyVersion
+ private fun readPlatformBuild(platformVersion: SupportedIJVersion): String {
+ val catalogFile = project.rootProject.file("gradle/libs.versions.toml")
+ val dependencyName =
+ when (platformVersion) {
+ SupportedIJVersion.IJ_232 -> "idea232"
+ SupportedIJVersion.IJ_233 -> "idea233"
+ }
+
+ val catalogDependencyLine =
+ catalogFile.useLines { lines -> lines.find { it.startsWith(dependencyName) } }
+ ?: throw GradleException(
+ "Unable to find IJP dependency '$dependencyName' in the catalog file '${catalogFile.path}'"
+ )
+
+ val dependencyVersion =
+ catalogDependencyLine.substringAfter(dependencyName).trimStart(' ', '=').trimEnd().trim('"')
+
+ if (!dependencyVersion.matches(versionRegex)) {
+ throw GradleException("Invalid IJP version found in version catalog: '$dependencyVersion'")
}
- private object VersionComparator : Comparator {
+ return dependencyVersion
+ }
- override fun compare(o1: String?, o2: String?): Int {
- if (o1 == o2) return 0
- if (o1 == null) return -1
- if (o2 == null) return 1
+ private object VersionComparator : Comparator {
- require(o1.isNotEmpty() && o1.all { it.isDigit() || it == '.' }) { "The first version is invalid: '$o1'" }
- require(o2.isNotEmpty() && o2.all { it.isDigit() || it == '.' }) { "The first version is invalid: '$o2'" }
+ override fun compare(o1: String?, o2: String?): Int {
+ if (o1 == o2) return 0
+ if (o1 == null) return -1
+ if (o2 == null) return 1
- val firstGroups = o1.split('.')
- val secondGroups = o2.split('.')
+ require(o1.isNotEmpty() && o1.all { it.isDigit() || it == '.' }) {
+ "The first version is invalid: '$o1'"
+ }
+ require(o2.isNotEmpty() && o2.all { it.isDigit() || it == '.' }) {
+ "The first version is invalid: '$o2'"
+ }
- require(firstGroups.size == 3) { "The first version is invalid: '$o1'" }
- require(secondGroups.size == 3) { "The second version is invalid: '$o2'" }
+ val firstGroups = o1.split('.')
+ val secondGroups = o2.split('.')
- val firstComparison = firstGroups[0].toInt().compareTo(secondGroups[0].toInt())
- if (firstComparison != 0) return firstComparison
+ require(firstGroups.size == 3) { "The first version is invalid: '$o1'" }
+ require(secondGroups.size == 3) { "The second version is invalid: '$o2'" }
- val secondComparison = firstGroups[1].toInt().compareTo(secondGroups[1].toInt())
- if (secondComparison != 0) return secondComparison
+ val firstComparison = firstGroups[0].toInt().compareTo(secondGroups[0].toInt())
+ if (firstComparison != 0) return firstComparison
- return firstGroups[2].toInt().compareTo(secondGroups[2].toInt())
- }
+ val secondComparison = firstGroups[1].toInt().compareTo(secondGroups[1].toInt())
+ if (secondComparison != 0) return secondComparison
+
+ return firstGroups[2].toInt().compareTo(secondGroups[2].toInt())
}
+ }
- private object ReleaseComparator : Comparator {
+ private object ReleaseComparator : Comparator {
- override fun compare(o1: ApiIdeaReleasesItem.Release?, o2: ApiIdeaReleasesItem.Release?): Int {
- if (o1 == o2) return 0
- if (o1 == null) return -1
- if (o2 == null) return 1
+ override fun compare(o1: ApiIdeaReleasesItem.Release?, o2: ApiIdeaReleasesItem.Release?): Int {
+ if (o1 == o2) return 0
+ if (o1 == null) return -1
+ if (o2 == null) return 1
- return VersionComparator.compare(o1.build, o2.build)
- }
+ return VersionComparator.compare(o1.build, o2.build)
}
+ }
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntUiThemeDescriptorReader.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntUiThemeDescriptorReader.kt
index e1d4a04f73..5ec7d5aaf2 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntUiThemeDescriptorReader.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntUiThemeDescriptorReader.kt
@@ -14,133 +14,146 @@ import kotlinx.serialization.json.JsonPrimitive
internal object IntUiThemeDescriptorReader {
- private val colorGroups = setOf("Grey", "Blue", "Green", "Red", "Yellow", "Orange", "Purple", "Teal")
- private val colorClassName = ClassName("androidx.compose.ui.graphics", "Color")
-
- fun readThemeFrom(
- themeDescriptor: IntellijThemeDescriptor,
- className: ClassName,
- ideaVersion: String,
- descriptorUrl: String,
- ) =
- FileSpec.builder(className).apply {
- indent(" ")
- addFileComment("Generated by the Jewel Int UI Palette Generator\n")
- addFileComment("Generated from the IntelliJ Platform version $ideaVersion\n")
- addFileComment("Source: $descriptorUrl")
-
- addImport(colorClassName.packageName, colorClassName.simpleName)
-
- addType(TypeSpec.objectBuilder(className).apply {
- addSuperinterface(ClassName.bestGuess("org.jetbrains.jewel.foundation.theme.ThemeDescriptor"))
-
- addProperty(
- PropertySpec.builder("isDark", Boolean::class, KModifier.OVERRIDE)
- .initializer("%L", themeDescriptor.dark)
- .build()
- )
-
- addProperty(
- PropertySpec.builder("name", String::class, KModifier.OVERRIDE)
- .initializer("\"%L (Int UI)\"", themeDescriptor.name)
- .build()
- )
-
- readColors(themeDescriptor.colors)
- readIcons(themeDescriptor)
- }.build())
- }.build()
-
- private val colorPaletteClassName =
- ClassName.bestGuess("org.jetbrains.jewel.foundation.theme.ThemeColorPalette")
- private val iconDataClassName =
- ClassName.bestGuess("org.jetbrains.jewel.foundation.theme.ThemeIconData")
-
- private fun TypeSpec.Builder.readColors(colors: Map) {
- val colorGroups = colors.entries.groupBy {
- it.key.replace("""\d+""".toRegex(), "")
- }.filterKeys {
- colorGroups.contains(it)
- }.map { (groupName, colors) ->
- // We assume color lists are in the same order as in colorGroups
- colors
- .map { (_, value) ->
- val colorHexString = value.replace("#", "0xFF")
- CodeBlock.of("Color(%L)", colorHexString)
- }
- .joinToCode(prefix = "\n${groupName.lowercase()} = listOf(\n", separator = ",\n", suffix = "\n)")
- }
-
- val rawMap = colors
- .map { (key, value) ->
- val colorHexString = value.replace("#", "0xFF")
- CodeBlock.of("%S to Color(%L)", key, colorHexString)
+ private val colorGroups =
+ setOf("Grey", "Blue", "Green", "Red", "Yellow", "Orange", "Purple", "Teal")
+ private val colorClassName = ClassName("androidx.compose.ui.graphics", "Color")
+
+ fun readThemeFrom(
+ themeDescriptor: IntellijThemeDescriptor,
+ className: ClassName,
+ ideaVersion: String,
+ descriptorUrl: String,
+ ) =
+ FileSpec.builder(className)
+ .apply {
+ indent(" ")
+ addFileComment("Generated by the Jewel Int UI Palette Generator\n")
+ addFileComment("Generated from the IntelliJ Platform version $ideaVersion\n")
+ addFileComment("Source: $descriptorUrl")
+
+ addImport(colorClassName.packageName, colorClassName.simpleName)
+
+ addType(
+ TypeSpec.objectBuilder(className)
+ .apply {
+ addSuperinterface(
+ ClassName.bestGuess("org.jetbrains.jewel.foundation.theme.ThemeDescriptor")
+ )
+
+ addProperty(
+ PropertySpec.builder("isDark", Boolean::class, KModifier.OVERRIDE)
+ .initializer("%L", themeDescriptor.dark)
+ .build()
+ )
+
+ addProperty(
+ PropertySpec.builder("name", String::class, KModifier.OVERRIDE)
+ .initializer("\"%L (Int UI)\"", themeDescriptor.name)
+ .build()
+ )
+
+ readColors(themeDescriptor.colors)
+ readIcons(themeDescriptor)
}
- .joinToCode(prefix = "\nrawMap = mapOf(\n", separator = ",\n", suffix = "\n)")
-
- addProperty(
- PropertySpec.builder("colors", colorPaletteClassName, KModifier.OVERRIDE)
- .initializer(
- "ThemeColorPalette(%L,\n%L\n)",
- colorGroups.joinToCode(","),
- rawMap
- )
- .build()
+ .build()
)
- }
-
- private fun TypeSpec.Builder.readIcons(theme: IntellijThemeDescriptor) {
- val iconOverrides = mutableMapOf()
- val colorPalette = mutableMapOf()
-
- for ((key, value) in theme.icons) {
- if (value is JsonPrimitive && value.isString) {
- iconOverrides += key to value.content
- } else if (value is JsonObject && key == "ColorPalette") {
- value.entries
- .mapNotNull {
- val pairValue = it.value
- if (pairValue is JsonPrimitive && pairValue.isString) {
- it.key to pairValue.content
- } else null
- }
- .forEach { colorPalette[it.first] = it.second }
+ }
+ .build()
+
+ private val colorPaletteClassName =
+ ClassName.bestGuess("org.jetbrains.jewel.foundation.theme.ThemeColorPalette")
+ private val iconDataClassName =
+ ClassName.bestGuess("org.jetbrains.jewel.foundation.theme.ThemeIconData")
+
+ private fun TypeSpec.Builder.readColors(colors: Map) {
+ val colorGroups =
+ colors.entries
+ .groupBy { it.key.replace("""\d+""".toRegex(), "") }
+ .filterKeys { colorGroups.contains(it) }
+ .map { (groupName, colors) ->
+ // We assume color lists are in the same order as in colorGroups
+ colors
+ .map { (_, value) ->
+ val colorHexString = value.replace("#", "0xFF")
+ CodeBlock.of("Color(%L)", colorHexString)
}
+ .joinToCode(
+ prefix = "\n${groupName.lowercase()} = listOf(\n",
+ separator = ",\n",
+ suffix = "\n)"
+ )
}
- val iconOverridesBlock = iconOverrides.toMapCodeBlock()
- val selectionColorPaletteBlock = theme.iconColorsOnSelection.toMapCodeBlock()
-
- addProperty(
- PropertySpec.builder("iconData", iconDataClassName, KModifier.OVERRIDE)
- .initializer(
- CodeBlock.of(
- "ThemeIconData(iconOverrides = \n%L,colorPalette = \n%L,\nselectionColorPalette = %L\n)",
- iconOverridesBlock,
- colorPalette.toMapCodeBlock(),
- selectionColorPaletteBlock,
- )
- )
- .build()
- )
+ val rawMap =
+ colors
+ .map { (key, value) ->
+ val colorHexString = value.replace("#", "0xFF")
+ CodeBlock.of("%S to Color(%L)", key, colorHexString)
+ }
+ .joinToCode(prefix = "\nrawMap = mapOf(\n", separator = ",\n", suffix = "\n)")
+
+ addProperty(
+ PropertySpec.builder("colors", colorPaletteClassName, KModifier.OVERRIDE)
+ .initializer("ThemeColorPalette(%L,\n%L\n)", colorGroups.joinToCode(","), rawMap)
+ .build()
+ )
+ }
+
+ private fun TypeSpec.Builder.readIcons(theme: IntellijThemeDescriptor) {
+ val iconOverrides = mutableMapOf()
+ val colorPalette = mutableMapOf()
+
+ for ((key, value) in theme.icons) {
+ if (value is JsonPrimitive && value.isString) {
+ iconOverrides += key to value.content
+ } else if (value is JsonObject && key == "ColorPalette") {
+ value.entries
+ .mapNotNull {
+ val pairValue = it.value
+ if (pairValue is JsonPrimitive && pairValue.isString) {
+ it.key to pairValue.content
+ } else null
+ }
+ .forEach { colorPalette[it.first] = it.second }
+ }
}
- private inline fun Map.toMapCodeBlock() =
- entries
- .map { (key, value) -> CodeBlock.of("\"%L\" to \"%L\"", key, value) }
- .joinToCode(prefix = "mapOf(", separator = ",\n", suffix = ")")
-
- private inline fun createOverrideStringMapProperty(name: String, values: Map) =
- PropertySpec.builder(
- name = name,
- type = Map::class.asTypeName()
- .parameterizedBy(K::class.asTypeName(), V::class.asTypeName()),
- KModifier.OVERRIDE
+ val iconOverridesBlock = iconOverrides.toMapCodeBlock()
+ val selectionColorPaletteBlock = theme.iconColorsOnSelection.toMapCodeBlock()
+
+ addProperty(
+ PropertySpec.builder("iconData", iconDataClassName, KModifier.OVERRIDE)
+ .initializer(
+ CodeBlock.of(
+ "ThemeIconData(iconOverrides = \n%L,colorPalette = \n%L,\nselectionColorPalette = %L\n)",
+ iconOverridesBlock,
+ colorPalette.toMapCodeBlock(),
+ selectionColorPaletteBlock,
+ )
)
- .initializer(
- values.entries
- .map { (key, value) -> CodeBlock.of("\"%L\" to \"%L\"", key, value) }
- .joinToCode(prefix = "mapOf(", separator = ",\n", suffix = ")")
- )
- .build()
+ .build()
+ )
+ }
+
+ private inline fun Map.toMapCodeBlock() =
+ entries
+ .map { (key, value) -> CodeBlock.of("\"%L\" to \"%L\"", key, value) }
+ .joinToCode(prefix = "mapOf(", separator = ",\n", suffix = ")")
+
+ private inline fun createOverrideStringMapProperty(
+ name: String,
+ values: Map
+ ) =
+ PropertySpec.builder(
+ name = name,
+ type =
+ Map::class.asTypeName().parameterizedBy(K::class.asTypeName(), V::class.asTypeName()),
+ KModifier.OVERRIDE
+ )
+ .initializer(
+ values.entries
+ .map { (key, value) -> CodeBlock.of("\"%L\" to \"%L\"", key, value) }
+ .joinToCode(prefix = "mapOf(", separator = ",\n", suffix = ")")
+ )
+ .build()
}
diff --git a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntelliJThemeGeneratorPlugin.kt b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntelliJThemeGeneratorPlugin.kt
index f3d9087656..8ea0da7e41 100644
--- a/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntelliJThemeGeneratorPlugin.kt
+++ b/buildSrc/src/main/kotlin/org/jetbrains/jewel/buildlogic/theme/IntelliJThemeGeneratorPlugin.kt
@@ -1,6 +1,7 @@
package org.jetbrains.jewel.buildlogic.theme
import com.squareup.kotlinpoet.ClassName
+import java.net.URL
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
@@ -14,69 +15,69 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
-import java.net.URL
-class ThemeGeneratorContainer(container: NamedDomainObjectContainer) : NamedDomainObjectContainer by container
+class ThemeGeneratorContainer(container: NamedDomainObjectContainer) :
+ NamedDomainObjectContainer by container
class ThemeGeneration(val name: String, project: Project) {
- val targetDir: DirectoryProperty = project.objects.directoryProperty()
- .convention(project.layout.buildDirectory.dir("generated/theme"))
- val ideaVersion = project.objects.property()
- .convention("232.6734")
- val themeClassName = project.objects.property()
- val themeFile = project.objects.property()
+ val targetDir: DirectoryProperty =
+ project.objects
+ .directoryProperty()
+ .convention(project.layout.buildDirectory.dir("generated/theme"))
+ val ideaVersion = project.objects.property().convention("232.6734")
+ val themeClassName = project.objects.property()
+ val themeFile = project.objects.property()
}
open class IntelliJThemeGeneratorTask : DefaultTask() {
- @get:OutputFile
- val outputFile: RegularFileProperty = project.objects.fileProperty()
+ @get:OutputFile val outputFile: RegularFileProperty = project.objects.fileProperty()
- @get:Input
- val ideaVersion = project.objects.property()
+ @get:Input val ideaVersion = project.objects.property()
- @get:Input
- val themeFile = project.objects.property()
+ @get:Input val themeFile = project.objects.property()
- @get:Input
- val themeClassName = project.objects.property()
+ @get:Input val themeClassName = project.objects.property()
- init {
- group = "jewel"
- }
+ init {
+ group = "jewel"
+ }
- @TaskAction
- fun generate() {
- val json = Json { ignoreUnknownKeys = true }
- val url = buildString {
- append("https://raw.githubusercontent.com/JetBrains/intellij-community/")
- append(ideaVersion.get())
- append("/")
- append(themeFile.get())
- }
+ @TaskAction
+ fun generate() {
+ val json = Json { ignoreUnknownKeys = true }
+ val url = buildString {
+ append("https://raw.githubusercontent.com/JetBrains/intellij-community/")
+ append(ideaVersion.get())
+ append("/")
+ append(themeFile.get())
+ }
- logger.lifecycle("Fetching theme descriptor from $url...")
- val themeDescriptor = URL(url).openStream()
- .use { json.decodeFromStream(it) }
+ logger.lifecycle("Fetching theme descriptor from $url...")
+ val themeDescriptor =
+ URL(url).openStream().use { json.decodeFromStream(it) }
- val className = ClassName.bestGuess(themeClassName.get())
- val file = IntUiThemeDescriptorReader.readThemeFrom(themeDescriptor, className, ideaVersion.get(), url)
+ val className = ClassName.bestGuess(themeClassName.get())
+ val file =
+ IntUiThemeDescriptorReader.readThemeFrom(themeDescriptor, className, ideaVersion.get(), url)
- val outputFile = outputFile.get().asFile
- logger.lifecycle("Theme descriptor for ${themeDescriptor.name} parsed and code generated into ${outputFile.path}")
- outputFile.bufferedWriter().use { file.writeTo(it) }
- }
+ val outputFile = outputFile.get().asFile
+ logger.lifecycle(
+ "Theme descriptor for ${themeDescriptor.name} parsed and code generated into ${outputFile.path}"
+ )
+ outputFile.bufferedWriter().use { file.writeTo(it) }
+ }
}
@Serializable
data class IntellijThemeDescriptor(
- val name: String,
- val author: String = "",
- val dark: Boolean = false,
- val editorScheme: String,
- val colors: Map = emptyMap(),
- val ui: Map = emptyMap(),
- val icons: Map = emptyMap(),
- val iconColorsOnSelection: Map = emptyMap(),
+ val name: String,
+ val author: String = "",
+ val dark: Boolean = false,
+ val editorScheme: String,
+ val colors: Map = emptyMap(),
+ val ui: Map = emptyMap(),
+ val icons: Map = emptyMap(),
+ val iconColorsOnSelection: Map = emptyMap(),
)
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt
index 4d685d9825..ba525c1ec2 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt
@@ -42,7 +42,8 @@ import java.awt.event.ComponentListener
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
-@Composable fun DecoratedWindow(
+@Composable
+public fun DecoratedWindow(
onCloseRequest: () -> Unit,
state: WindowState = rememberWindowState(),
visible: Boolean = true,
@@ -87,43 +88,44 @@ import java.awt.event.WindowEvent
var decoratedWindowState by remember { mutableStateOf(DecoratedWindowState.of(window)) }
DisposableEffect(window) {
- val adapter = object : WindowAdapter(), ComponentListener {
- override fun windowActivated(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
+ val adapter =
+ object : WindowAdapter(), ComponentListener {
+ override fun windowActivated(e: WindowEvent?) {
+ decoratedWindowState = DecoratedWindowState.of(window)
+ }
+
+ override fun windowDeactivated(e: WindowEvent?) {
+ decoratedWindowState = DecoratedWindowState.of(window)
+ }
+
+ override fun windowIconified(e: WindowEvent?) {
+ decoratedWindowState = DecoratedWindowState.of(window)
+ }
+
+ override fun windowDeiconified(e: WindowEvent?) {
+ decoratedWindowState = DecoratedWindowState.of(window)
+ }
+
+ override fun windowStateChanged(e: WindowEvent) {
+ decoratedWindowState = DecoratedWindowState.of(window)
+ }
+
+ override fun componentResized(e: ComponentEvent?) {
+ decoratedWindowState = DecoratedWindowState.of(window)
+ }
+
+ override fun componentMoved(e: ComponentEvent?) {
+ // Empty
+ }
+
+ override fun componentShown(e: ComponentEvent?) {
+ // Empty
+ }
+
+ override fun componentHidden(e: ComponentEvent?) {
+ // Empty
+ }
}
-
- override fun windowDeactivated(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowIconified(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowDeiconified(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowStateChanged(e: WindowEvent) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun componentResized(e: ComponentEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun componentMoved(e: ComponentEvent?) {
- // Empty
- }
-
- override fun componentShown(e: ComponentEvent?) {
- // Empty
- }
-
- override fun componentHidden(e: ComponentEvent?) {
- // Empty
- }
- }
window.addWindowListener(adapter)
window.addWindowStateListener(adapter)
window.addComponentListener(adapter)
@@ -134,43 +136,55 @@ import java.awt.event.WindowEvent
}
}
- val undecoratedWindowBorder = if (undecorated && !decoratedWindowState.isMaximized) {
- Modifier.border(
- Stroke.Alignment.Inside,
- style.metrics.borderWidth,
- style.colors.borderFor(decoratedWindowState).value,
- RectangleShape,
- ).padding(style.metrics.borderWidth)
- } else {
- Modifier
- }
+ val undecoratedWindowBorder =
+ if (undecorated && !decoratedWindowState.isMaximized) {
+ Modifier.border(
+ Stroke.Alignment.Inside,
+ style.metrics.borderWidth,
+ style.colors.borderFor(decoratedWindowState).value,
+ RectangleShape,
+ )
+ .padding(style.metrics.borderWidth)
+ } else {
+ Modifier
+ }
CompositionLocalProvider(
LocalTitleBarInfo provides TitleBarInfo(title, icon),
) {
- Layout({
- val scope = object : DecoratedWindowScope {
- override val state: DecoratedWindowState
- get() = decoratedWindowState
-
- override val window: ComposeWindow get() = this@Window.window
- }
- scope.content()
- }, modifier = undecoratedWindowBorder.trackWindowActivation(window), measurePolicy = DecoratedWindowMeasurePolicy)
+ Layout(
+ {
+ val scope =
+ object : DecoratedWindowScope {
+ override val state: DecoratedWindowState
+ get() = decoratedWindowState
+
+ override val window: ComposeWindow
+ get() = this@Window.window
+ }
+ scope.content()
+ },
+ modifier = undecoratedWindowBorder.trackWindowActivation(window),
+ measurePolicy = DecoratedWindowMeasurePolicy,
+ )
}
}
}
-@Stable interface DecoratedWindowScope : FrameWindowScope {
+@Stable
+public interface DecoratedWindowScope : FrameWindowScope {
override val window: ComposeWindow
- val state: DecoratedWindowState
+ public val state: DecoratedWindowState
}
private object DecoratedWindowMeasurePolicy : MeasurePolicy {
- override fun MeasureScope.measure(measurables: List, constraints: Constraints): MeasureResult {
+ override fun MeasureScope.measure(
+ measurables: List,
+ constraints: Constraints,
+ ): MeasureResult {
if (measurables.isEmpty()) {
return layout(
constraints.minWidth,
@@ -197,7 +211,8 @@ private object DecoratedWindowMeasurePolicy : MeasurePolicy {
for (it in measurables) {
if (it.layoutId.toString().startsWith(TITLE_BAR_COMPONENT_LAYOUT_ID_PREFIX)) continue
- val placeable = it.measure(contentConstraints.offset(vertical = -titleBarHeight - titleBarBorderHeight))
+ val placeable =
+ it.measure(contentConstraints.offset(vertical = -titleBarHeight - titleBarBorderHeight))
measuredPlaceable += placeable
}
@@ -205,69 +220,70 @@ private object DecoratedWindowMeasurePolicy : MeasurePolicy {
titleBarPlaceable?.placeRelative(0, 0)
titleBarBorderPlaceable?.placeRelative(0, titleBarHeight)
- measuredPlaceable.forEach {
- it.placeRelative(0, titleBarHeight + titleBarBorderHeight)
- }
+ measuredPlaceable.forEach { it.placeRelative(0, titleBarHeight + titleBarBorderHeight) }
}
}
}
-@Immutable @JvmInline
-value class DecoratedWindowState(val state: ULong) {
+@Immutable
+@JvmInline
+public value class DecoratedWindowState(public val state: ULong) {
- @Stable val isActive: Boolean
+ public val isActive: Boolean
get() = state and Active != 0UL
- @Stable val isFullscreen: Boolean
+ public val isFullscreen: Boolean
get() = state and Fullscreen != 0UL
- @Stable val isMinimized: Boolean
+ public val isMinimized: Boolean
get() = state and Minimize != 0UL
- @Stable val isMaximized: Boolean
+ public val isMaximized: Boolean
get() = state and Maximize != 0UL
- fun copy(
+ public fun copy(
fullscreen: Boolean = isFullscreen,
minimized: Boolean = isMinimized,
maximized: Boolean = isMaximized,
active: Boolean = isActive,
- ) = of(
- fullscreen = fullscreen,
- minimized = minimized,
- maximized = maximized,
- active = active,
- )
+ ): DecoratedWindowState =
+ of(
+ fullscreen = fullscreen,
+ minimized = minimized,
+ maximized = maximized,
+ active = active,
+ )
- override fun toString() = "${javaClass.simpleName}(isFullscreen=$isFullscreen, isActive=$isActive)"
+ override fun toString(): String =
+ "${javaClass.simpleName}(isFullscreen=$isFullscreen, isActive=$isActive)"
- companion object {
+ public companion object {
- val Active = 1UL shl 0
- val Fullscreen = 1UL shl 1
- val Minimize = 1UL shl 2
- val Maximize = 1UL shl 3
+ public val Active: ULong = 1UL shl 0
+ public val Fullscreen: ULong = 1UL shl 1
+ public val Minimize: ULong = 1UL shl 2
+ public val Maximize: ULong = 1UL shl 3
- fun of(
+ public fun of(
fullscreen: Boolean = false,
minimized: Boolean = false,
maximized: Boolean = false,
active: Boolean = true,
- ) = DecoratedWindowState(
- state = (if (fullscreen) Fullscreen else 0UL) or
- (if (minimized) Minimize else 0UL) or
- (if (maximized) Maximize else 0UL) or
- (if (active) Active else 0UL),
- )
+ ): DecoratedWindowState =
+ DecoratedWindowState(
+ (if (fullscreen) Fullscreen else 0UL) or
+ (if (minimized) Minimize else 0UL) or
+ (if (maximized) Maximize else 0UL) or
+ (if (active) Active else 0UL),
+ )
- fun of(
- window: ComposeWindow,
- ) = of(
- window.placement == WindowPlacement.Fullscreen,
- window.isMinimized,
- window.placement == WindowPlacement.Maximized,
- window.isActive,
- )
+ public fun of(window: ComposeWindow): DecoratedWindowState =
+ of(
+ fullscreen = window.placement == WindowPlacement.Fullscreen,
+ minimized = window.isMinimized,
+ maximized = window.placement == WindowPlacement.Maximized,
+ active = window.isActive,
+ )
}
}
@@ -276,6 +292,9 @@ internal data class TitleBarInfo(
val icon: Painter?,
)
-internal val LocalTitleBarInfo = compositionLocalOf {
- error("CompositionLocal LocalTitleBarInfo not provided, TitleBar must be used in DecoratedWindow")
-}
+internal val LocalTitleBarInfo =
+ compositionLocalOf {
+ error(
+ "CompositionLocal LocalTitleBarInfo not provided, TitleBar must be used in DecoratedWindow",
+ )
+ }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt
index 9a66b25fa3..93d672e515 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt
@@ -8,12 +8,10 @@ import org.jetbrains.jewel.window.styling.LocalDecoratedWindowStyle
import org.jetbrains.jewel.window.styling.LocalTitleBarStyle
import org.jetbrains.jewel.window.styling.TitleBarStyle
-val JewelTheme.Companion.defaultTitleBarStyle: TitleBarStyle
- @Composable
- @ReadOnlyComposable
+public val JewelTheme.Companion.defaultTitleBarStyle: TitleBarStyle
+ @Composable @ReadOnlyComposable
get() = LocalTitleBarStyle.current
-val JewelTheme.Companion.defaultDecoratedWindowStyle: DecoratedWindowStyle
- @Composable
- @ReadOnlyComposable
+public val JewelTheme.Companion.defaultDecoratedWindowStyle: DecoratedWindowStyle
+ @Composable @ReadOnlyComposable
get() = LocalDecoratedWindowStyle.current
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt
index c8057a29a6..0aeace864a 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt
@@ -38,9 +38,15 @@ internal fun DecoratedWindowScope.TitleBarOnLinux(
val viewConfig = LocalViewConfiguration.current
TitleBarImpl(
modifier.onPointerEvent(PointerEventType.Press, PointerEventPass.Main) {
- if (this.currentEvent.button == PointerButton.Primary && this.currentEvent.changes.any { changed -> !changed.isConsumed }) {
+ if (
+ this.currentEvent.button == PointerButton.Primary &&
+ this.currentEvent.changes.any { changed -> !changed.isConsumed }
+ ) {
JBR.getWindowMove()?.startMovingTogetherWithMouse(window, MouseEvent.BUTTON1)
- if (System.currentTimeMillis() - lastPress in viewConfig.doubleTapMinTimeMillis..viewConfig.doubleTapTimeoutMillis) {
+ if (
+ System.currentTimeMillis() - lastPress in
+ viewConfig.doubleTapMinTimeMillis..viewConfig.doubleTapTimeoutMillis
+ ) {
if (state.isMaximized) {
window.extendedState = Frame.NORMAL
} else {
@@ -54,22 +60,33 @@ internal fun DecoratedWindowScope.TitleBarOnLinux(
style,
{ _, _ -> PaddingValues(0.dp) },
) { state ->
- CloseButton({
- window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING))
- }, state, style)
+ CloseButton(
+ { window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING)) },
+ state,
+ style,
+ )
if (state.isMaximized) {
- ControlButton({
- window.extendedState = Frame.NORMAL
- }, state, style.icons.restoreButton, "Restore")
+ ControlButton(
+ { window.extendedState = Frame.NORMAL },
+ state,
+ style.icons.restoreButton,
+ "Restore",
+ )
} else {
- ControlButton({
- window.extendedState = Frame.MAXIMIZED_BOTH
- }, state, style.icons.maximizeButton, "Maximize")
+ ControlButton(
+ { window.extendedState = Frame.MAXIMIZED_BOTH },
+ state,
+ style.icons.maximizeButton,
+ "Maximize",
+ )
}
- ControlButton({
- window.extendedState = Frame.ICONIFIED
- }, state, style.icons.minimizeButton, "Minimize")
+ ControlButton(
+ { window.extendedState = Frame.ICONIFIED },
+ state,
+ style.icons.minimizeButton,
+ "Minimize",
+ )
content(state)
}
}
@@ -94,12 +111,13 @@ private fun TitleBarScope.ControlButton(
) {
IconButton(
onClick,
- Modifier.align(Alignment.End)
- .focusable(false)
- .size(style.metrics.titlePaneButtonSize),
+ Modifier.align(Alignment.End).focusable(false).size(style.metrics.titlePaneButtonSize),
style = iconButtonStyle,
) {
- Icon(painterProvider.getPainter(if (state.isActive) PainterHint else Inactive).value, description)
+ Icon(
+ painterProvider.getPainter(if (state.isActive) PainterHint else Inactive).value,
+ description,
+ )
}
}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt
index d81074faa7..c51e9b43d5 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt
@@ -15,11 +15,12 @@ import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.window.styling.TitleBarStyle
import org.jetbrains.jewel.window.utils.macos.MacUtil
-fun Modifier.newFullscreenControls(newControls: Boolean = true): Modifier {
+public fun Modifier.newFullscreenControls(newControls: Boolean = true): Modifier {
return then(
NewFullscreenControlsElement(
newControls,
- inspectorInfo = debugInspectorInfo {
+ inspectorInfo =
+ debugInspectorInfo {
name = "newFullscreenControls"
value = newControls
},
@@ -62,13 +63,14 @@ internal fun DecoratedWindowScope.TitleBarOnMacOs(
style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
content: @Composable TitleBarScope.(DecoratedWindowState) -> Unit,
) {
- val newFullscreenControls = modifier.foldOut(false) { e, r ->
- if (e is NewFullscreenControlsElement) {
- e.newControls
- } else {
- r
+ val newFullscreenControls =
+ modifier.foldOut(false) { e, r ->
+ if (e is NewFullscreenControlsElement) {
+ e.newControls
+ } else {
+ r
+ }
}
- }
if (newFullscreenControls) {
System.setProperty("apple.awt.newFullScreeControls", true.toString())
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt
index 6897c351b5..531ed86486 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt
@@ -40,25 +40,26 @@ internal fun DecoratedWindowScope.TitleBarOnWindows(
)
}
-internal fun Modifier.customTitleBarMouseEventHandler(titleBar: CustomTitleBar): Modifier = this.pointerInput(Unit) {
- val currentContext = currentCoroutineContext()
- awaitPointerEventScope {
- var inUserControl = false
- while (currentContext.isActive) {
- val event = awaitPointerEvent(PointerEventPass.Main)
- event.changes.forEach {
- if (!it.isConsumed && !inUserControl) {
- titleBar.forceHitTest(false)
- } else {
- if (event.type == PointerEventType.Press) {
- inUserControl = true
+internal fun Modifier.customTitleBarMouseEventHandler(titleBar: CustomTitleBar): Modifier =
+ this.pointerInput(Unit) {
+ val currentContext = currentCoroutineContext()
+ awaitPointerEventScope {
+ var inUserControl = false
+ while (currentContext.isActive) {
+ val event = awaitPointerEvent(PointerEventPass.Main)
+ event.changes.forEach {
+ if (!it.isConsumed && !inUserControl) {
+ titleBar.forceHitTest(false)
+ } else {
+ if (event.type == PointerEventType.Press) {
+ inUserControl = true
+ }
+ if (event.type == PointerEventType.Release) {
+ inUserControl = false
+ }
+ titleBar.forceHitTest(true)
}
- if (event.type == PointerEventType.Release) {
- inUserControl = false
- }
- titleBar.forceHitTest(true)
}
}
}
}
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt
index 4a2a8174c5..9c6c6b6b7d 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt
@@ -56,7 +56,8 @@ internal const val TITLE_BAR_LAYOUT_ID = "__TITLE_BAR_CONTENT__"
internal const val TITLE_BAR_BORDER_LAYOUT_ID = "__TITLE_BAR_BORDER__"
-@Composable fun DecoratedWindowScope.TitleBar(
+@Composable
+public fun DecoratedWindowScope.TitleBar(
modifier: Modifier = Modifier,
gradientStartColor: Color = Color.Unspecified,
style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
@@ -66,7 +67,8 @@ internal const val TITLE_BAR_BORDER_LAYOUT_ID = "__TITLE_BAR_BORDER__"
DesktopPlatform.Linux -> TitleBarOnLinux(modifier, gradientStartColor, style, content)
DesktopPlatform.Windows -> TitleBarOnWindows(modifier, gradientStartColor, style, content)
DesktopPlatform.MacOS -> TitleBarOnMacOs(modifier, gradientStartColor, style, content)
- DesktopPlatform.Unknown -> error("TitleBar is not supported on this platform(${System.getProperty("os.name")})")
+ DesktopPlatform.Unknown ->
+ error("TitleBar is not supported on this platform(${System.getProperty("os.name")})")
}
}
@@ -84,21 +86,22 @@ internal fun DecoratedWindowScope.TitleBarImpl(
val density = LocalDensity.current
- val backgroundBrush = remember(background, gradientStartColor) {
- if (gradientStartColor.isUnspecified) {
- SolidColor(background)
- } else {
- with(density) {
- Brush.horizontalGradient(
- 0.0f to background,
- 0.5f to gradientStartColor,
- 1.0f to background,
- startX = style.metrics.gradientStartX.toPx(),
- endX = style.metrics.gradientEndX.toPx(),
- )
+ val backgroundBrush =
+ remember(background, gradientStartColor) {
+ if (gradientStartColor.isUnspecified) {
+ SolidColor(background)
+ } else {
+ with(density) {
+ Brush.horizontalGradient(
+ 0.0f to background,
+ 0.5f to gradientStartColor,
+ 1.0f to background,
+ startX = style.metrics.gradientStartX.toPx(),
+ endX = style.metrics.gradientEndX.toPx(),
+ )
+ }
}
}
- }
Layout(
{
@@ -113,24 +116,27 @@ internal fun DecoratedWindowScope.TitleBarImpl(
}
}
},
- modifier.background(backgroundBrush)
+ modifier
+ .background(backgroundBrush)
.focusProperties { canFocus = false }
.layoutId(TITLE_BAR_LAYOUT_ID)
.height(style.metrics.height)
- .onSizeChanged {
- with(density) {
- applyTitleBar(it.height.toDp(), state)
- }
- }
+ .onSizeChanged { with(density) { applyTitleBar(it.height.toDp(), state) } }
.fillMaxWidth(),
- measurePolicy = rememberTitleBarMeasurePolicy(
+ measurePolicy =
+ rememberTitleBarMeasurePolicy(
window,
state,
applyTitleBar,
),
)
- Spacer(Modifier.layoutId(TITLE_BAR_BORDER_LAYOUT_ID).height(1.dp).fillMaxWidth().background(style.colors.border))
+ Spacer(
+ Modifier.layoutId(TITLE_BAR_BORDER_LAYOUT_ID)
+ .height(1.dp)
+ .fillMaxWidth()
+ .background(style.colors.border),
+ )
}
internal class TitleBarMeasurePolicy(
@@ -139,7 +145,10 @@ internal class TitleBarMeasurePolicy(
private val applyTitleBar: (Dp, DecoratedWindowState) -> PaddingValues,
) : MeasurePolicy {
- override fun MeasureScope.measure(measurables: List, constraints: Constraints): MeasureResult {
+ override fun MeasureScope.measure(
+ measurables: List,
+ constraints: Constraints,
+ ): MeasureResult {
if (measurables.isEmpty()) {
return layout(
constraints.minWidth,
@@ -179,9 +188,11 @@ internal class TitleBarMeasurePolicy(
if (state.isFullscreen) {
MacUtil.updateFullScreenButtons(window)
}
- val placeableGroups = measuredPlaceable.groupBy { (measurable, _) ->
- (measurable.parentData as? TitleBarChildDataNode)?.horizontalAlignment ?: Alignment.CenterHorizontally
- }
+ val placeableGroups =
+ measuredPlaceable.groupBy { (measurable, _) ->
+ (measurable.parentData as? TitleBarChildDataNode)?.horizontalAlignment
+ ?: Alignment.CenterHorizontally
+ }
var headUsedSpace = leftInset
var trailerUsedSpace = rightInset
@@ -240,13 +251,13 @@ internal fun rememberTitleBarMeasurePolicy(
}
}
-interface TitleBarScope {
+public interface TitleBarScope {
- val title: String
+ public val title: String
- val icon: Painter?
+ public val icon: Painter?
- @Stable fun Modifier.align(alignment: Alignment.Horizontal): Modifier
+ @Stable public fun Modifier.align(alignment: Alignment.Horizontal): Modifier
}
private class TitleBarScopeImpl(
@@ -258,7 +269,8 @@ private class TitleBarScopeImpl(
return then(
TitleBarChildDataElement(
alignment,
- inspectorInfo = debugInspectorInfo {
+ inspectorInfo =
+ debugInspectorInfo {
name = "align"
value = alignment
},
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt
index b7ef0d0efb..bf2756a8bb 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt
@@ -2,6 +2,8 @@ package org.jetbrains.jewel.window.styling
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ProvidableCompositionLocal
+import androidx.compose.runtime.State
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
@@ -11,39 +13,39 @@ import org.jetbrains.jewel.window.DecoratedWindowState
@Immutable
@GenerateDataFunctions
-class DecoratedWindowStyle(
- val colors: DecoratedWindowColors,
- val metrics: DecoratedWindowMetrics,
+public class DecoratedWindowStyle(
+ public val colors: DecoratedWindowColors,
+ public val metrics: DecoratedWindowMetrics,
) {
- companion object
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class DecoratedWindowColors(
- val border: Color,
- val borderInactive: Color,
+public class DecoratedWindowColors(
+ public val border: Color,
+ public val borderInactive: Color,
) {
@Composable
- fun borderFor(state: DecoratedWindowState) = rememberUpdatedState(
- when {
- !state.isActive -> borderInactive
- else -> border
- },
- )
-
- companion object
+ public fun borderFor(state: DecoratedWindowState): State =
+ rememberUpdatedState(
+ when {
+ !state.isActive -> borderInactive
+ else -> border
+ },
+ )
+
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class DecoratedWindowMetrics(val borderWidth: Dp) {
+public class DecoratedWindowMetrics(public val borderWidth: Dp) {
- companion object
+ public companion object
}
-val LocalDecoratedWindowStyle = staticCompositionLocalOf {
- error("No DecoratedWindowStyle provided")
-}
+public val LocalDecoratedWindowStyle: ProvidableCompositionLocal =
+ staticCompositionLocalOf { error("No DecoratedWindowStyle provided") }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt
index 60e5d634e1..1a81412d8b 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt
@@ -2,7 +2,9 @@ package org.jetbrains.jewel.window.styling
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
@@ -16,82 +18,83 @@ import org.jetbrains.jewel.window.DecoratedWindowState
@Stable
@GenerateDataFunctions
-class TitleBarStyle(
- val colors: TitleBarColors,
- val metrics: TitleBarMetrics,
- val icons: TitleBarIcons,
- val dropdownStyle: DropdownStyle,
- val iconButtonStyle: IconButtonStyle,
- val paneButtonStyle: IconButtonStyle,
- val paneCloseButtonStyle: IconButtonStyle,
+public class TitleBarStyle(
+ public val colors: TitleBarColors,
+ public val metrics: TitleBarMetrics,
+ public val icons: TitleBarIcons,
+ public val dropdownStyle: DropdownStyle,
+ public val iconButtonStyle: IconButtonStyle,
+ public val paneButtonStyle: IconButtonStyle,
+ public val paneCloseButtonStyle: IconButtonStyle,
) {
- companion object
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class TitleBarColors(
- val background: Color,
- val inactiveBackground: Color,
- val content: Color,
- val border: Color,
+public class TitleBarColors(
+ public val background: Color,
+ public val inactiveBackground: Color,
+ public val content: Color,
+ public val border: Color,
- // The background color for newControlButtons(three circles in left top corner) in MacOS fullscreen mode
- val fullscreenControlButtonsBackground: Color,
+ // The background color for newControlButtons(three circles in left top corner) in MacOS
+ // fullscreen mode
+ public val fullscreenControlButtonsBackground: Color,
// The hover and press background color for window control buttons(minimize, maximize) in Linux
- val titlePaneButtonHoveredBackground: Color,
- val titlePaneButtonPressedBackground: Color,
+ public val titlePaneButtonHoveredBackground: Color,
+ public val titlePaneButtonPressedBackground: Color,
// The hover and press background color for window close button in Linux
- val titlePaneCloseButtonHoveredBackground: Color,
- val titlePaneCloseButtonPressedBackground: Color,
+ public val titlePaneCloseButtonHoveredBackground: Color,
+ public val titlePaneCloseButtonPressedBackground: Color,
// The hover and press background color for IconButtons in title bar content
- val iconButtonHoveredBackground: Color,
- val iconButtonPressedBackground: Color,
+ public val iconButtonHoveredBackground: Color,
+ public val iconButtonPressedBackground: Color,
// The hover and press background color for Dropdown in title bar content
- val dropdownPressedBackground: Color,
- val dropdownHoveredBackground: Color,
+ public val dropdownPressedBackground: Color,
+ public val dropdownHoveredBackground: Color,
) {
@Composable
- fun backgroundFor(state: DecoratedWindowState) = rememberUpdatedState(
- when {
- !state.isActive -> inactiveBackground
- else -> background
- },
- )
-
- companion object
+ public fun backgroundFor(state: DecoratedWindowState): State =
+ rememberUpdatedState(
+ when {
+ !state.isActive -> inactiveBackground
+ else -> background
+ },
+ )
+
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class TitleBarMetrics(
- val height: Dp,
- val gradientStartX: Dp,
- val gradientEndX: Dp,
- val titlePaneButtonSize: DpSize,
+public class TitleBarMetrics(
+ public val height: Dp,
+ public val gradientStartX: Dp,
+ public val gradientEndX: Dp,
+ public val titlePaneButtonSize: DpSize,
) {
- companion object
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class TitleBarIcons(
- val minimizeButton: PainterProvider,
- val maximizeButton: PainterProvider,
- val restoreButton: PainterProvider,
- val closeButton: PainterProvider,
+public class TitleBarIcons(
+ public val minimizeButton: PainterProvider,
+ public val maximizeButton: PainterProvider,
+ public val restoreButton: PainterProvider,
+ public val closeButton: PainterProvider,
) {
- companion object
+ public companion object
}
-val LocalTitleBarStyle = staticCompositionLocalOf {
- error("No TitleBarStyle provided")
-}
+public val LocalTitleBarStyle: ProvidableCompositionLocal =
+ staticCompositionLocalOf { error("No TitleBarStyle provided") }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt
index b426693491..51872dc474 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt
@@ -1,14 +1,15 @@
package org.jetbrains.jewel.window.utils
-enum class DesktopPlatform {
+public enum class DesktopPlatform {
Linux,
Windows,
MacOS,
Unknown,
;
- companion object {
- val Current: DesktopPlatform by lazy {
+ public companion object {
+
+ public val Current: DesktopPlatform by lazy {
val name = System.getProperty("os.name")
when {
name?.startsWith("Linux") == true -> Linux
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt
index c44cc077b2..7e136b0f65 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt
@@ -10,13 +10,12 @@ internal object JnaLoader {
private var loaded: Boolean? = null
private val logger = Logger.getLogger(JnaLoader::class.java.simpleName)
- @Synchronized fun load() {
+ @Synchronized
+ fun load() {
if (loaded == null) {
loaded = false
try {
- val time = measureTimeMillis {
- Native.POINTER_SIZE
- }
+ val time = measureTimeMillis { Native.POINTER_SIZE }
logger.info("JNA library (${Native.POINTER_SIZE shl 3}-bit) loaded in $time ms")
loaded = true
} catch (@Suppress("TooGenericExceptionCaught") t: Throwable) {
@@ -33,7 +32,8 @@ internal object JnaLoader {
}
}
- @get:Synchronized val isLoaded: Boolean
+ @get:Synchronized
+ val isLoaded: Boolean
get() {
if (loaded == null) {
load()
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt
index 2dd553873b..eb7c36b3a8 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt
@@ -20,13 +20,9 @@ internal object UnsafeAccessing {
}
}
- val desktopModule by lazy {
- ModuleLayer.boot().findModule("java.desktop").get()
- }
+ val desktopModule by lazy { ModuleLayer.boot().findModule("java.desktop").get() }
- val ownerModule: Module by lazy {
- this.javaClass.module
- }
+ val ownerModule: Module by lazy { this.javaClass.module }
private val isAccessibleFieldOffset: Long? by lazy {
try {
@@ -38,17 +34,20 @@ internal object UnsafeAccessing {
private val implAddOpens by lazy {
try {
- Module::class.java.getDeclaredMethod(
- "implAddOpens",
- String::class.java,
- Module::class.java,
- ).accessible()
+ Module::class
+ .java
+ .getDeclaredMethod(
+ "implAddOpens",
+ String::class.java,
+ Module::class.java,
+ )
+ .accessible()
} catch (_: Throwable) {
null
}
}
- fun assignAccessibility(obj: AccessibleObject) {
+ public fun assignAccessibility(obj: AccessibleObject) {
try {
val theUnsafe = unsafe as? Unsafe ?: return
val offset = isAccessibleFieldOffset ?: return
@@ -58,11 +57,9 @@ internal object UnsafeAccessing {
}
}
- fun assignAccessibility(module: Module, packages: List) {
+ public fun assignAccessibility(module: Module, packages: List) {
try {
- packages.forEach {
- implAddOpens?.invoke(module, it, ownerModule)
- }
+ packages.forEach { implAddOpens?.invoke(module, it, ownerModule) }
} catch (_: Throwable) {
// ignore
}
@@ -72,13 +69,9 @@ internal object UnsafeAccessing {
var first = false
- @Volatile
- var second: Any? = null
+ @Volatile var second: Any? = null
}
}
-internal fun T.accessible(): T {
- return apply {
- UnsafeAccessing.assignAccessibility(this)
- }
-}
+internal fun T.accessible(): T =
+ apply { UnsafeAccessing.assignAccessibility(this) }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt
index 193b29c7eb..e634f0c5b9 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt
@@ -35,20 +35,20 @@ internal object Foundation {
private val myObjcMsgSend: Function? by lazy {
try {
- (Proxy.getInvocationHandler(myFoundationLibrary) as Library.Handler).nativeLibrary.getFunction("objc_msgSend")
+ (Proxy.getInvocationHandler(myFoundationLibrary) as Library.Handler)
+ .nativeLibrary
+ .getFunction("objc_msgSend")
} catch (_: Throwable) {
null
}
}
- /**
- * Get the ID of the NSClass with className
- */
- fun getObjcClass(className: String?): ID? = myFoundationLibrary?.objc_getClass(className)
+ /** Get the ID of the NSClass with className */
+ public fun getObjcClass(className: String?): ID? = myFoundationLibrary?.objc_getClass(className)
- fun getProtocol(name: String?): ID? = myFoundationLibrary?.objc_getProtocol(name)
+ public fun getProtocol(name: String?): ID? = myFoundationLibrary?.objc_getProtocol(name)
- fun createSelector(s: String?): Pointer? = myFoundationLibrary?.sel_registerName(s)
+ public fun createSelector(s: String?): Pointer? = myFoundationLibrary?.sel_registerName(s)
private fun prepInvoke(id: ID?, selector: Pointer?, args: Array): Array {
val invokArgs = arrayOfNulls(args.size + 2)
@@ -59,17 +59,19 @@ internal object Foundation {
}
// objc_msgSend is called with the calling convention of the target method
- // on x86_64 this does not make a difference, but arm64 uses a different calling convention for varargs
+ // on x86_64 this does not make a difference, but arm64 uses a different calling convention for
+ // varargs
// it is therefore important to not call objc_msgSend as a vararg function
operator fun invoke(id: ID?, selector: Pointer?, vararg args: Any?): ID =
ID(myObjcMsgSend?.invokeLong(prepInvoke(id, selector, args)) ?: 0)
/**
- * Invokes the given vararg selector.
- * Expects `NSArray arrayWithObjects:(id), ...` like signature, i.e. exactly one fixed argument, followed by varargs.
+ * Invokes the given vararg selector. Expects `NSArray arrayWithObjects:(id), ...` like signature,
+ * i.e. exactly one fixed argument, followed by varargs.
*/
- fun invokeVarArg(id: ID?, selector: Pointer?, vararg args: Any?): ID {
- // c functions and objc methods have at least 1 fixed argument, we therefore need to separate out the first argument
+ public fun invokeVarArg(id: ID?, selector: Pointer?, vararg args: Any?): ID {
+ // c functions and objc methods have at least 1 fixed argument, we therefore need to separate
+ // out the first argument
return myFoundationLibrary?.objc_msgSend(
id,
selector,
@@ -81,10 +83,10 @@ internal object Foundation {
operator fun invoke(cls: String?, selector: String?, vararg args: Any?): ID =
invoke(getObjcClass(cls), createSelector(selector), *args)
- fun invokeVarArg(cls: String?, selector: String?, vararg args: Any?): ID =
+ public fun invokeVarArg(cls: String?, selector: String?, vararg args: Any?): ID =
invokeVarArg(getObjcClass(cls), createSelector(selector), *args)
- fun safeInvoke(stringCls: String?, stringSelector: String?, vararg args: Any?): ID {
+ public fun safeInvoke(stringCls: String?, stringSelector: String?, vararg args: Any?): ID {
val cls = getObjcClass(stringCls)
val selector = createSelector(stringSelector)
if (!invoke(cls, "respondsToSelector:", selector).booleanValue()) {
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt
index a4c93807e2..9ca7d1138c 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt
@@ -5,11 +5,16 @@ import com.sun.jna.Library
import com.sun.jna.Pointer
internal interface FoundationLibrary : Library {
- fun NSLog(pString: Pointer?, thing: Any?)
- fun NSFullUserName(): ID?
- fun objc_allocateClassPair(supercls: ID?, name: String?, extraBytes: Int): ID?
- fun objc_registerClassPair(cls: ID?)
- fun CFStringCreateWithBytes(
+
+ public fun NSLog(pString: Pointer?, thing: Any?)
+
+ public fun NSFullUserName(): ID?
+
+ public fun objc_allocateClassPair(supercls: ID?, name: String?, extraBytes: Int): ID?
+
+ public fun objc_registerClassPair(cls: ID?)
+
+ public fun CFStringCreateWithBytes(
allocator: Pointer?,
bytes: ByteArray?,
byteCount: Int,
@@ -17,36 +22,65 @@ internal interface FoundationLibrary : Library {
isExternalRepresentation: Byte,
): ID?
- fun CFStringGetCString(theString: ID?, buffer: ByteArray?, bufferSize: Int, encoding: Int): Byte
- fun CFStringGetLength(theString: ID?): Int
- fun CFStringConvertNSStringEncodingToEncoding(nsEncoding: Long): Long
- fun CFStringConvertEncodingToIANACharSetName(cfEncoding: Long): ID?
- fun CFStringConvertIANACharSetNameToEncoding(encodingName: ID?): Long
- fun CFStringConvertEncodingToNSStringEncoding(cfEncoding: Long): Long
- fun CFRetain(cfTypeRef: ID?)
- fun CFRelease(cfTypeRef: ID?)
- fun CFGetRetainCount(cfTypeRef: Pointer?): Int
- fun objc_getClass(className: String?): ID?
- fun objc_getProtocol(name: String?): ID?
- fun class_createInstance(pClass: ID?, extraBytes: Int): ID?
- fun sel_registerName(selectorName: String?): Pointer?
- fun class_replaceMethod(cls: ID?, selName: Pointer?, impl: Callback?, types: String?): ID?
- fun objc_getMetaClass(name: String?): ID?
+ public fun CFStringGetCString(
+ theString: ID?,
+ buffer: ByteArray?,
+ bufferSize: Int,
+ encoding: Int,
+ ): Byte
+
+ public fun CFStringGetLength(theString: ID?): Int
+
+ public fun CFStringConvertNSStringEncodingToEncoding(nsEncoding: Long): Long
+
+ public fun CFStringConvertEncodingToIANACharSetName(cfEncoding: Long): ID?
+
+ public fun CFStringConvertIANACharSetNameToEncoding(encodingName: ID?): Long
+
+ public fun CFStringConvertEncodingToNSStringEncoding(cfEncoding: Long): Long
+
+ public fun CFRetain(cfTypeRef: ID?)
+
+ public fun CFRelease(cfTypeRef: ID?)
+
+ public fun CFGetRetainCount(cfTypeRef: Pointer?): Int
+
+ public fun objc_getClass(className: String?): ID?
+
+ public fun objc_getProtocol(name: String?): ID?
+
+ public fun class_createInstance(pClass: ID?, extraBytes: Int): ID?
+
+ public fun sel_registerName(selectorName: String?): Pointer?
+
+ public fun class_replaceMethod(cls: ID?, selName: Pointer?, impl: Callback?, types: String?): ID?
+
+ public fun objc_getMetaClass(name: String?): ID?
/**
- * Note: Vararg version. Should only be used only for selectors with a single fixed argument followed by varargs.
+ * Note: Vararg version. Should only be used only for selectors with a single fixed argument
+ * followed by varargs.
*/
- fun objc_msgSend(receiver: ID?, selector: Pointer?, firstArg: Any?, vararg args: Any?): ID?
- fun class_respondsToSelector(cls: ID?, selName: Pointer?): Boolean
- fun class_addMethod(cls: ID?, selName: Pointer?, imp: Callback?, types: String?): Boolean
- fun class_addMethod(cls: ID?, selName: Pointer?, imp: ID?, types: String?): Boolean
- fun class_addProtocol(aClass: ID?, protocol: ID?): Boolean
- fun class_isMetaClass(cls: ID?): Boolean
- fun NSStringFromSelector(selector: Pointer?): ID?
- fun NSStringFromClass(aClass: ID?): ID?
- fun objc_getClass(clazz: Pointer?): Pointer?
-
- companion object {
+ public fun objc_msgSend(receiver: ID?, selector: Pointer?, firstArg: Any?, vararg args: Any?): ID?
+
+ public fun class_respondsToSelector(cls: ID?, selName: Pointer?): Boolean
+
+ public fun class_addMethod(cls: ID?, selName: Pointer?, imp: Callback?, types: String?): Boolean
+
+ public fun class_addMethod(cls: ID?, selName: Pointer?, imp: ID?, types: String?): Boolean
+
+ public fun class_addProtocol(aClass: ID?, protocol: ID?): Boolean
+
+ public fun class_isMetaClass(cls: ID?): Boolean
+
+ public fun NSStringFromSelector(selector: Pointer?): ID?
+
+ public fun NSStringFromClass(aClass: ID?): ID?
+
+ public fun objc_getClass(clazz: Pointer?): Pointer?
+
+ public companion object {
+
const val kCFStringEncodingMacRoman = 0
const val kCFStringEncodingWindowsLatin1 = 0x0500
const val kCFStringEncodingISOLatin1 = 0x0201
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt
index df74e25e7f..c1d6128e48 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt
@@ -2,16 +2,14 @@ package org.jetbrains.jewel.window.utils.macos
import com.sun.jna.NativeLong
-/**
- * Could be an address in memory (if pointer to a class or method) or a
- * value (like 0 or 1)
- */
+/** Could be an address in memory (if pointer to a class or method) or a value (like 0 or 1) */
internal class ID : NativeLong {
constructor()
+
constructor(peer: Long) : super(peer)
- fun booleanValue(): Boolean = toInt() != 0
+ public fun booleanValue(): Boolean = toInt() != 0
override fun toByte(): Byte = toInt().toByte()
@@ -22,9 +20,8 @@ internal class ID : NativeLong {
@Suppress("RedundantOverride") // Without this, we get a SOE
override fun toInt(): Int = super.toInt()
- companion object {
+ public companion object {
- @JvmField
- val NIL = ID(0L)
+ @JvmField val NIL = ID(0L)
}
}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt
index 248cb170a1..d87e26b77e 100644
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt
+++ b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt
@@ -24,7 +24,7 @@ internal object MacUtil {
}
}
- fun getWindowFromJavaWindow(w: Window?): ID {
+ public fun getWindowFromJavaWindow(w: Window?): ID {
if (w == null) {
return ID.NIL
}
@@ -43,11 +43,12 @@ internal object MacUtil {
return ID.NIL
}
- fun getPlatformWindow(w: Window): Any? {
+ public fun getPlatformWindow(w: Window): Any? {
try {
val awtAccessor = Class.forName("sun.awt.AWTAccessor")
val componentAccessor = awtAccessor.getMethod("getComponentAccessor").invoke(null)
- val getPeer = componentAccessor.javaClass.getMethod("getPeer", Component::class.java).accessible()
+ val getPeer =
+ componentAccessor.javaClass.getMethod("getPeer", Component::class.java).accessible()
val peer = getPeer.invoke(componentAccessor, w)
if (peer != null) {
val cWindowPeerClass: Class<*> = peer.javaClass
@@ -69,11 +70,16 @@ internal object MacUtil {
return null
}
- fun updateColors(w: Window) {
+ public fun updateColors(w: Window) {
SwingUtilities.invokeLater {
val window = getWindowFromJavaWindow(w)
val delegate = Foundation.invoke(window, "delegate")
- if (Foundation.invoke(delegate, "respondsToSelector:", Foundation.createSelector("updateColors"))
+ if (
+ Foundation.invoke(
+ delegate,
+ "respondsToSelector:",
+ Foundation.createSelector("updateColors"),
+ )
.booleanValue()
) {
Foundation.invoke(delegate, "updateColors")
@@ -81,7 +87,7 @@ internal object MacUtil {
}
}
- fun updateFullScreenButtons(w: Window) {
+ public fun updateFullScreenButtons(w: Window) {
SwingUtilities.invokeLater {
val selector = Foundation.createSelector("updateFullScreenButtons")
val window = getWindowFromJavaWindow(w)
diff --git a/foundation/build.gradle.kts b/foundation/build.gradle.kts
index f8eda8d687..1e835ea528 100644
--- a/foundation/build.gradle.kts
+++ b/foundation/build.gradle.kts
@@ -7,7 +7,8 @@ plugins {
alias(libs.plugins.composeDesktop)
}
-private val composeVersion get() = ComposeBuildConfig.composeVersion
+private val composeVersion
+ get() = ComposeBuildConfig.composeVersion
dependencies {
api("org.jetbrains.compose.foundation:foundation-desktop:$composeVersion")
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Compatibility.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Compatibility.kt
index a9a89a4dd0..18fe0c6baf 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Compatibility.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Compatibility.kt
@@ -1,16 +1,15 @@
package org.jetbrains.jewel.foundation
/**
- * Enables the new compositing strategy for rendering directly into Swing
- * Graphics. This fixes z-order problems and artifacts on resizing, but
- * has a performance penalty when using infinitely repeating animations.
+ * Enables the new compositing strategy for rendering directly into Swing Graphics. This fixes
+ * z-order problems and artifacts on resizing, but has a performance penalty when using infinitely
+ * repeating animations.
*
- * We assume the majority of our users will want this flag to be on, so
- * this convenience function is provided to that end. Make sure you call
- * it **before** you initialize any Compose content. The function is
- * idempotent and extremely cheap, so you can call it on any entry point.
+ * We assume the majority of our users will want this flag to be on, so this convenience function is
+ * provided to that end. Make sure you call it **before** you initialize any Compose content. The
+ * function is idempotent and extremely cheap, so you can call it on any entry point.
*/
@ExperimentalJewelApi
-fun enableNewSwingCompositing() {
+public fun enableNewSwingCompositing() {
System.setProperty("compose.swing.render.on.graphics", "true")
}
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/ExperimentalJewelApi.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/ExperimentalJewelApi.kt
index 9fb4e89038..84ce0adc5d 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/ExperimentalJewelApi.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/ExperimentalJewelApi.kt
@@ -2,11 +2,11 @@ package org.jetbrains.jewel.foundation
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
- message = "This is an experimental API for Jewel and is likely to change before becoming " +
- "stable.",
+ message =
+ "This is an experimental API for Jewel and is likely to change before becoming " + "stable.",
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
)
-annotation class ExperimentalJewelApi
+public annotation class ExperimentalJewelApi
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GenerateDataFunctions.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GenerateDataFunctions.kt
index 2f95513f27..2e4bf843a4 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GenerateDataFunctions.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GenerateDataFunctions.kt
@@ -1,10 +1,10 @@
package org.jetbrains.jewel.foundation
/**
- * Instructs the Poko compiler plugin to generate equals, hashcode,
- * and toString functions for the class it's attached to.
+ * Instructs the Poko compiler plugin to generate equals, hashcode, and toString functions for the
+ * class it's attached to.
*
* See https://github.com/JetBrains/jewel/issues/83
*/
@Target(AnnotationTarget.CLASS)
-annotation class GenerateDataFunctions
+public annotation class GenerateDataFunctions
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalColors.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalColors.kt
index a531624fc0..f613e2e81e 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalColors.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalColors.kt
@@ -1,45 +1,45 @@
package org.jetbrains.jewel.foundation
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
@Immutable
@GenerateDataFunctions
-class GlobalColors(
- val borders: BorderColors,
- val outlines: OutlineColors,
- val infoContent: Color,
- val paneBackground: Color,
+public class GlobalColors(
+ public val borders: BorderColors,
+ public val outlines: OutlineColors,
+ public val infoContent: Color,
+ public val paneBackground: Color,
) {
- companion object
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class BorderColors(
- val normal: Color,
- val focused: Color,
- val disabled: Color,
+public class BorderColors(
+ public val normal: Color,
+ public val focused: Color,
+ public val disabled: Color,
) {
- companion object
+ public companion object
}
@Immutable
@GenerateDataFunctions
-class OutlineColors(
- val focused: Color,
- val focusedWarning: Color,
- val focusedError: Color,
- val warning: Color,
- val error: Color,
+public class OutlineColors(
+ public val focused: Color,
+ public val focusedWarning: Color,
+ public val focusedError: Color,
+ public val warning: Color,
+ public val error: Color,
) {
- companion object
+ public companion object
}
-val LocalGlobalColors = staticCompositionLocalOf {
- error("No GlobalColors provided")
-}
+public val LocalGlobalColors: ProvidableCompositionLocal =
+ staticCompositionLocalOf { error("No GlobalColors provided") }
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalMetrics.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalMetrics.kt
index ced8ecd3ff..135184c80d 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalMetrics.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/GlobalMetrics.kt
@@ -1,19 +1,19 @@
package org.jetbrains.jewel.foundation
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.Dp
@Immutable
@GenerateDataFunctions
-class GlobalMetrics(
- val outlineWidth: Dp,
- val rowHeight: Dp,
+public class GlobalMetrics(
+ public val outlineWidth: Dp,
+ public val rowHeight: Dp,
) {
- companion object
+ public companion object
}
-val LocalGlobalMetrics = staticCompositionLocalOf {
- error("No GlobalMetrics provided")
-}
+public val LocalGlobalMetrics: ProvidableCompositionLocal =
+ staticCompositionLocalOf { error("No GlobalMetrics provided") }
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/InternalJewelApi.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/InternalJewelApi.kt
index faed75434c..049fee1418 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/InternalJewelApi.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/InternalJewelApi.kt
@@ -10,4 +10,4 @@ package org.jetbrains.jewel.foundation
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.PROPERTY,
)
-annotation class InternalJewelApi
+public annotation class InternalJewelApi
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/RoundRectUtil.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/RoundRectUtil.kt
index b971fd65a4..b0c40e04dd 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/RoundRectUtil.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/RoundRectUtil.kt
@@ -9,10 +9,14 @@ internal fun RoundRect.grow(delta: Float) =
top = top - delta,
right = right + delta,
bottom = bottom + delta,
- topLeftCornerRadius = CornerRadius(topLeftCornerRadius.x + delta, topLeftCornerRadius.y + delta),
- topRightCornerRadius = CornerRadius(topRightCornerRadius.x + delta, topRightCornerRadius.y + delta),
- bottomLeftCornerRadius = CornerRadius(bottomLeftCornerRadius.x + delta, bottomLeftCornerRadius.y + delta),
- bottomRightCornerRadius = CornerRadius(bottomRightCornerRadius.x + delta, bottomRightCornerRadius.y + delta),
+ topLeftCornerRadius =
+ CornerRadius(topLeftCornerRadius.x + delta, topLeftCornerRadius.y + delta),
+ topRightCornerRadius =
+ CornerRadius(topRightCornerRadius.x + delta, topRightCornerRadius.y + delta),
+ bottomLeftCornerRadius =
+ CornerRadius(bottomLeftCornerRadius.x + delta, bottomLeftCornerRadius.y + delta),
+ bottomRightCornerRadius =
+ CornerRadius(bottomRightCornerRadius.x + delta, bottomRightCornerRadius.y + delta),
)
internal fun RoundRect.shrink(delta: Float) =
@@ -21,10 +25,14 @@ internal fun RoundRect.shrink(delta: Float) =
top = top + delta,
right = right - delta,
bottom = bottom - delta,
- topLeftCornerRadius = CornerRadius(topLeftCornerRadius.x - delta, topLeftCornerRadius.y - delta),
- topRightCornerRadius = CornerRadius(topRightCornerRadius.x - delta, topRightCornerRadius.y - delta),
- bottomLeftCornerRadius = CornerRadius(bottomLeftCornerRadius.x - delta, bottomLeftCornerRadius.y - delta),
- bottomRightCornerRadius = CornerRadius(bottomRightCornerRadius.x - delta, bottomRightCornerRadius.y - delta),
+ topLeftCornerRadius =
+ CornerRadius(topLeftCornerRadius.x - delta, topLeftCornerRadius.y - delta),
+ topRightCornerRadius =
+ CornerRadius(topRightCornerRadius.x - delta, topRightCornerRadius.y - delta),
+ bottomLeftCornerRadius =
+ CornerRadius(bottomLeftCornerRadius.x - delta, bottomLeftCornerRadius.y - delta),
+ bottomRightCornerRadius =
+ CornerRadius(bottomRightCornerRadius.x - delta, bottomRightCornerRadius.y - delta),
)
internal fun RoundRect.hasAtLeastOneNonRoundedCorner() =
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Stroke.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Stroke.kt
index 0dd5caa674..c7e7a3a743 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Stroke.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/Stroke.kt
@@ -7,44 +7,59 @@ import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.isUnspecified
import androidx.compose.ui.unit.Dp
-sealed class Stroke {
+public sealed class Stroke {
+
@Immutable
- object None : Stroke() {
+ public object None : Stroke() {
override fun toString(): String = "None"
}
@Immutable
@GenerateDataFunctions
- class Solid internal constructor(
- val width: Dp,
- val color: Color,
- val alignment: Alignment,
- val expand: Dp,
+ public class Solid
+ internal constructor(
+ public val width: Dp,
+ public val color: Color,
+ public val alignment: Alignment,
+ public val expand: Dp,
) : Stroke()
@Immutable
@GenerateDataFunctions
- class Brush internal constructor(
- val width: Dp,
- val brush: androidx.compose.ui.graphics.Brush,
- val alignment: Alignment,
- val expand: Dp,
+ public class Brush
+ internal constructor(
+ public val width: Dp,
+ public val brush: androidx.compose.ui.graphics.Brush,
+ public val alignment: Alignment,
+ public val expand: Dp,
) : Stroke()
- enum class Alignment {
- Inside, Center, Outside
+ public enum class Alignment {
+ Inside,
+ Center,
+ Outside,
}
}
-fun Stroke(width: Dp, color: Color, alignment: Stroke.Alignment, expand: Dp = Dp.Unspecified): Stroke {
+public fun Stroke(
+ width: Dp,
+ color: Color,
+ alignment: Stroke.Alignment,
+ expand: Dp = Dp.Unspecified,
+): Stroke {
if (width.value == 0f) return Stroke.None
if (color.isUnspecified) return Stroke.None
return Stroke.Solid(width, color, alignment, expand)
}
-fun Stroke(width: Dp, brush: Brush, alignment: Stroke.Alignment, expand: Dp = Dp.Unspecified): Stroke {
+public fun Stroke(
+ width: Dp,
+ brush: Brush,
+ alignment: Stroke.Alignment,
+ expand: Dp = Dp.Unspecified,
+): Stroke {
if (width.value == 0f) return Stroke.None
return when (brush) {
is SolidColor -> {
@@ -54,7 +69,6 @@ fun Stroke(width: Dp, brush: Brush, alignment: Stroke.Alignment, expand: Dp = Dp
Stroke.Solid(width, brush.value, alignment, expand)
}
}
-
else -> Stroke.Brush(width, brush, alignment, expand)
}
}
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/Keybindings.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/Keybindings.kt
index a21e00227e..5f7e0e3a05 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/Keybindings.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/Keybindings.kt
@@ -11,13 +11,13 @@ import androidx.compose.ui.input.pointer.isCtrlPressed
import androidx.compose.ui.input.pointer.isMetaPressed
import androidx.compose.ui.input.pointer.isShiftPressed
-interface SelectableColumnKeybindings {
+public interface SelectableColumnKeybindings {
- val KeyEvent.isContiguousSelectionKeyPressed: Boolean
+ public val KeyEvent.isContiguousSelectionKeyPressed: Boolean
- val PointerKeyboardModifiers.isContiguousSelectionKeyPressed: Boolean
+ public val PointerKeyboardModifiers.isContiguousSelectionKeyPressed: Boolean
- val KeyEvent.isMultiSelectionKeyPressed: Boolean
+ public val KeyEvent.isMultiSelectionKeyPressed: Boolean
val PointerKeyboardModifiers.isMultiSelectionKeyPressed: Boolean
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableColumnOnKeyEvent.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableColumnOnKeyEvent.kt
index e23c931d84..6cf5b96b68 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableColumnOnKeyEvent.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableColumnOnKeyEvent.kt
@@ -4,14 +4,15 @@ import org.jetbrains.jewel.foundation.lazy.SelectableLazyListKey.Selectable
import kotlin.math.max
import kotlin.math.min
-interface SelectableColumnOnKeyEvent {
+public interface SelectableColumnOnKeyEvent {
- val keybindings: SelectableColumnKeybindings
+ public val keybindings: SelectableColumnKeybindings
- /**
- * Select First Node
- */
- fun onSelectFirstItem(allKeys: List, state: SelectableLazyListState) {
+ /** Select First Node */
+ public fun onSelectFirstItem(
+ allKeys: List,
+ state: SelectableLazyListState,
+ ) {
val firstSelectable = allKeys.withIndex().firstOrNull { it.value is Selectable }
if (firstSelectable != null) {
state.selectedKeys = listOf(firstSelectable.value.key)
@@ -19,36 +20,33 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Extend Selection to First Node inherited from Move Caret to Text Start with Selection
- */
- fun onExtendSelectionToFirst(keys: List, state: SelectableLazyListState) {
- state.lastActiveItemIndex
- ?.let {
- val iterator = keys.listIterator(it)
- val list = buildList {
- while (iterator.hasPrevious()) {
- val previous = iterator.previous()
- if (previous is Selectable) {
- add(previous.key)
- state.lastActiveItemIndex = (iterator.previousIndex() + 1).coerceAtMost(keys.size)
- }
+ /** Extend Selection to First Node inherited from Move Caret to Text Start with Selection */
+ public fun onExtendSelectionToFirst(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
+ state.lastActiveItemIndex?.let {
+ val iterator = keys.listIterator(it)
+ val list = buildList {
+ while (iterator.hasPrevious()) {
+ val previous = iterator.previous()
+ if (previous is Selectable) {
+ add(previous.key)
+ state.lastActiveItemIndex = (iterator.previousIndex() + 1).coerceAtMost(keys.size)
}
}
- if (list.isNotEmpty()) {
- state.selectedKeys =
- state.selectedKeys
- .toMutableList()
- .also { selectionList -> selectionList.addAll(list) }
- }
}
+ if (list.isNotEmpty()) {
+ state.selectedKeys =
+ state.selectedKeys.toMutableList().also { selectionList -> selectionList.addAll(list) }
+ }
+ }
}
- /**
- * Select Last Node inherited from Move Caret to Text End
- */
- fun onSelectLastItem(keys: List, state: SelectableLazyListState) {
- keys.withIndex()
+ /** Select Last Node inherited from Move Caret to Text End */
+ public fun onSelectLastItem(keys: List, state: SelectableLazyListState) {
+ keys
+ .withIndex()
.lastOrNull { it.value is Selectable }
?.let {
state.selectedKeys = listOf(it)
@@ -56,10 +54,11 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Extend Selection to Last Node inherited from Move Caret to Text End with Selection
- */
- fun onExtendSelectionToLastItem(keys: List, state: SelectableLazyListState) {
+ /** Extend Selection to Last Node inherited from Move Caret to Text End with Selection */
+ public fun onExtendSelectionToLastItem(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
state.lastActiveItemIndex?.let {
val list = mutableListOf(state.selectedKeys)
keys.subList(it, keys.lastIndex).forEachIndexed { index, selectableLazyListKey ->
@@ -72,10 +71,11 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Select Previous Node inherited from Up
- */
- fun onSelectPreviousItem(keys: List, state: SelectableLazyListState) {
+ /** Select Previous Node inherited from Up */
+ public fun onSelectPreviousItem(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
state.lastActiveItemIndex?.let { lastActiveIndex ->
if (lastActiveIndex == 0) return@let
keys
@@ -91,10 +91,11 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Extend Selection with Previous Node inherited from Up with Selection
- */
- fun onExtendSelectionWithPreviousItem(keys: List, state: SelectableLazyListState) {
+ /** Extend Selection with Previous Node inherited from Up with Selection */
+ public fun onExtendSelectionWithPreviousItem(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
state.lastActiveItemIndex?.let { lastActiveIndex ->
if (lastActiveIndex == 0) return@let
keys
@@ -110,10 +111,8 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Select Next Node inherited from Down
- */
- fun onSelectNextItem(keys: List, state: SelectableLazyListState) {
+ /** Select Next Node inherited from Down */
+ public fun onSelectNextItem(keys: List, state: SelectableLazyListState) {
state.lastActiveItemIndex?.let { lastActiveIndex ->
if (lastActiveIndex == keys.lastIndex) return@let
keys
@@ -127,10 +126,11 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Extend Selection with Next Node inherited from Down with Selection
- */
- fun onExtendSelectionWithNextItem(keys: List, state: SelectableLazyListState) {
+ /** Extend Selection with Next Node inherited from Down with Selection */
+ public fun onExtendSelectionWithNextItem(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
// todo we need deselect if we are changing direction
state.lastActiveItemIndex?.let { lastActiveIndex ->
if (lastActiveIndex == keys.lastIndex) return@let
@@ -145,78 +145,75 @@ interface SelectableColumnOnKeyEvent {
}
}
- /**
- * Scroll Page Up and Select Node inherited from Page Up
- */
- fun onScrollPageUpAndSelectItem(keys: List, state: SelectableLazyListState) {
+ /** Scroll Page Up and Select Node inherited from Page Up */
+ public fun onScrollPageUpAndSelectItem(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
val visibleSize = state.layoutInfo.visibleItemsInfo.size
val targetIndex = max((state.lastActiveItemIndex ?: 0) - visibleSize, 0)
state.selectedKeys = listOf(keys[targetIndex].key)
state.lastActiveItemIndex = targetIndex
}
- /**
- * Scroll Page Up and Extend Selection inherited from Page Up with Selection
- */
- fun onScrollPageUpAndExtendSelection(keys: List, state: SelectableLazyListState) {
+ /** Scroll Page Up and Extend Selection inherited from Page Up with Selection */
+ public fun onScrollPageUpAndExtendSelection(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
val visibleSize = state.layoutInfo.visibleItemsInfo.size
val targetIndex = max((state.lastActiveItemIndex ?: 0) - visibleSize, 0)
val newSelectionList =
- keys.subList(targetIndex, (state.lastActiveItemIndex ?: 0))
+ keys
+ .subList(targetIndex, (state.lastActiveItemIndex ?: 0))
.withIndex()
.filter { it.value is Selectable }
- .let {
- state.selectedKeys + it.map { selectableKey -> selectableKey.value.key }
- }
+ .let { state.selectedKeys + it.map { selectableKey -> selectableKey.value.key } }
state.selectedKeys = newSelectionList
state.lastActiveItemIndex = targetIndex
}
- /**
- * Scroll Page Down and Select Node inherited from Page Down
- */
- fun onScrollPageDownAndSelectItem(keys: List, state: SelectableLazyListState) {
+ /** Scroll Page Down and Select Node inherited from Page Down */
+ public fun onScrollPageDownAndSelectItem(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
val visibleSize = state.layoutInfo.visibleItemsInfo.size
val targetIndex = min((state.lastActiveItemIndex ?: 0) + visibleSize, keys.lastIndex)
state.selectedKeys = listOf(keys[targetIndex].key)
state.lastActiveItemIndex = targetIndex
}
- /**
- * Scroll Page Down and Extend Selection inherited from Page Down with Selection
- */
- fun onScrollPageDownAndExtendSelection(keys: List, state: SelectableLazyListState) {
+ /** Scroll Page Down and Extend Selection inherited from Page Down with Selection */
+ public fun onScrollPageDownAndExtendSelection(
+ keys: List,
+ state: SelectableLazyListState,
+ ) {
val visibleSize = state.layoutInfo.visibleItemsInfo.size
val targetIndex = min((state.lastActiveItemIndex ?: 0) + visibleSize, keys.lastIndex)
val newSelectionList =
- keys.subList(state.lastActiveItemIndex ?: 0, targetIndex)
- .filterIsInstance()
- .let {
- state.selectedKeys + it.map { selectableKey -> selectableKey.key }
- }
+ keys.subList(state.lastActiveItemIndex ?: 0, targetIndex).filterIsInstance().let {
+ state.selectedKeys + it.map { selectableKey -> selectableKey.key }
+ }
state.selectedKeys = newSelectionList
state.lastActiveItemIndex = targetIndex
}
- /**
- * Edit In Item
- */
- fun onEdit() {
- // ij with this shortcut just focus the first element with issue
+ /** Edit In Item */
+ public fun onEdit() {
+ // ij with this shortcut focuses the first element with issue
// unavailable here
}
- /**
- * Select All
- */
- fun onSelectAll(keys: List, state: SelectableLazyListState) {
+ /** Select All */
+ public fun onSelectAll(keys: List, state: SelectableLazyListState) {
state.selectedKeys = keys.filterIsInstance().map { it.key }
}
}
-open class DefaultSelectableOnKeyEvent(
+public open class DefaultSelectableOnKeyEvent(
override val keybindings: SelectableColumnKeybindings,
) : SelectableColumnOnKeyEvent {
- companion object : DefaultSelectableOnKeyEvent(DefaultSelectableColumnKeybindings)
+ public companion object : DefaultSelectableOnKeyEvent(DefaultSelectableColumnKeybindings)
}
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyColumn.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyColumn.kt
index fd0d66b2cd..09ed9da3f5 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyColumn.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyColumn.kt
@@ -30,19 +30,17 @@ import org.jetbrains.jewel.foundation.lazy.tree.DefaultSelectableLazyColumnKeyAc
import org.jetbrains.jewel.foundation.lazy.tree.KeyActions
import org.jetbrains.jewel.foundation.lazy.tree.PointerEventActions
-/**
- * A composable that displays a scrollable and selectable list of items in
- * a column arrangement.
- */
+/** A composable that displays a scrollable and selectable list of items in a column arrangement. */
@Composable
-fun SelectableLazyColumn(
+public fun SelectableLazyColumn(
modifier: Modifier = Modifier,
selectionMode: SelectionMode = SelectionMode.Multiple,
state: SelectableLazyListState = rememberSelectableLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
onSelectedIndexesChanged: (List) -> Unit = {},
- verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
+ verticalArrangement: Arrangement.Vertical =
+ if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
keyActions: KeyActions = DefaultSelectableLazyColumnKeyActions,
@@ -51,41 +49,30 @@ fun SelectableLazyColumn(
content: SelectableLazyListScope.() -> Unit,
) {
val scope = rememberCoroutineScope()
- val container = SelectableLazyListScopeContainer()
- .apply(content)
+ val container = SelectableLazyListScopeContainer().apply(content)
- val keys = remember(container) {
- container.getKeys()
- }
+ val keys = remember(container) { container.getKeys() }
var isFocused by remember { mutableStateOf(false) }
fun evaluateIndexes(): List {
val keyToIndexMap = keys.withIndex().associateBy({ it.value.key }, { it.index })
- return state.selectedKeys
- .mapNotNull { selected -> keyToIndexMap[selected] }
- .sorted()
+ return state.selectedKeys.mapNotNull { selected -> keyToIndexMap[selected] }.sorted()
}
- remember(state.selectedKeys) {
- onSelectedIndexesChanged(evaluateIndexes())
- }
+ remember(state.selectedKeys) { onSelectedIndexesChanged(evaluateIndexes()) }
val focusRequester = remember { FocusRequester() }
LazyColumn(
- modifier = modifier
+ modifier =
+ modifier
.onFocusChanged { isFocused = it.hasFocus }
.focusRequester(focusRequester)
.focusable(interactionSource = interactionSource)
.onPreviewKeyEvent { event ->
if (state.lastActiveItemIndex != null) {
- val actionHandled = keyActions
- .handleOnKeyEvent(event, keys, state, selectionMode)
- .invoke(event)
+ val actionHandled =
+ keyActions.handleOnKeyEvent(event, keys, state, selectionMode).invoke(event)
if (actionHandled) {
- scope.launch {
- state.lastActiveItemIndex?.let {
- state.scrollToItem(it)
- }
- }
+ scope.launch { state.lastActiveItemIndex?.let { state.scrollToItem(it) } }
}
}
true
@@ -99,76 +86,82 @@ fun SelectableLazyColumn(
) {
container.getEntries().forEach { entry ->
when (entry) {
- is Entry.Item -> item(entry.key, entry.contentType) {
- val itemScope = SelectableLazyItemScope(
- isSelected = entry.key in state.selectedKeys,
- isActive = isFocused,
- )
- if (keys.any { it.key == entry.key && it is SelectableLazyListKey.Selectable }) {
- Box(
- modifier = Modifier.selectable(
- requester = focusRequester,
- keybindings = keyActions.keybindings,
- actionHandler = pointerEventActions,
- selectionMode = selectionMode,
- selectableState = state,
- allKeys = keys,
- itemKey = entry.key,
- ),
- ) {
+ is Entry.Item ->
+ item(entry.key, entry.contentType) {
+ val itemScope =
+ SelectableLazyItemScope(
+ isSelected = entry.key in state.selectedKeys,
+ isActive = isFocused,
+ )
+ if (keys.any { it.key == entry.key && it is SelectableLazyListKey.Selectable }) {
+ Box(
+ modifier =
+ Modifier.selectable(
+ requester = focusRequester,
+ keybindings = keyActions.keybindings,
+ actionHandler = pointerEventActions,
+ selectionMode = selectionMode,
+ selectableState = state,
+ allKeys = keys,
+ itemKey = entry.key,
+ ),
+ ) {
+ entry.content.invoke(itemScope)
+ }
+ } else {
entry.content.invoke(itemScope)
}
- } else {
- entry.content.invoke(itemScope)
}
- }
-
- is Entry.Items -> items(
- count = entry.count,
- key = { entry.key(entry.startIndex + it) },
- contentType = { entry.contentType(it) },
- ) { index ->
- val itemScope = SelectableLazyItemScope(entry.key(index) in state.selectedKeys, isFocused)
- if (keys.any { it.key == entry.key(index) && it is SelectableLazyListKey.Selectable }) {
- Box(
- modifier = Modifier.selectable(
- requester = focusRequester,
- keybindings = keyActions.keybindings,
- actionHandler = pointerEventActions,
- selectionMode = selectionMode,
- selectableState = state,
- allKeys = keys,
- itemKey = entry.key(index),
- ),
- ) {
+ is Entry.Items ->
+ items(
+ count = entry.count,
+ key = { entry.key(entry.startIndex + it) },
+ contentType = { entry.contentType(it) },
+ ) { index ->
+ val itemScope =
+ SelectableLazyItemScope(entry.key(index) in state.selectedKeys, isFocused)
+ if (keys.any { it.key == entry.key(index) && it is SelectableLazyListKey.Selectable }) {
+ Box(
+ modifier =
+ Modifier.selectable(
+ requester = focusRequester,
+ keybindings = keyActions.keybindings,
+ actionHandler = pointerEventActions,
+ selectionMode = selectionMode,
+ selectableState = state,
+ allKeys = keys,
+ itemKey = entry.key(index),
+ ),
+ ) {
+ entry.itemContent.invoke(itemScope, index)
+ }
+ } else {
entry.itemContent.invoke(itemScope, index)
}
- } else {
- entry.itemContent.invoke(itemScope, index)
}
- }
-
- is Entry.StickyHeader -> stickyHeader(entry.key, entry.contentType) {
- val itemScope = SelectableLazyItemScope(entry.key in state.selectedKeys, isFocused)
- if (keys.any { it.key == entry.key && it is SelectableLazyListKey.Selectable }) {
- Box(
- modifier = Modifier.selectable(
- keybindings = keyActions.keybindings,
- actionHandler = pointerEventActions,
- selectionMode = selectionMode,
- selectableState = state,
- allKeys = keys,
- itemKey = entry.key,
- ),
- ) {
- entry.content.invoke(itemScope)
- }
- } else {
- SelectableLazyItemScope(entry.key in state.selectedKeys, isFocused).apply {
- entry.content.invoke(itemScope)
+ is Entry.StickyHeader ->
+ stickyHeader(entry.key, entry.contentType) {
+ val itemScope = SelectableLazyItemScope(entry.key in state.selectedKeys, isFocused)
+ if (keys.any { it.key == entry.key && it is SelectableLazyListKey.Selectable }) {
+ Box(
+ modifier =
+ Modifier.selectable(
+ keybindings = keyActions.keybindings,
+ actionHandler = pointerEventActions,
+ selectionMode = selectionMode,
+ selectableState = state,
+ allKeys = keys,
+ itemKey = entry.key,
+ ),
+ ) {
+ entry.content.invoke(itemScope)
+ }
+ } else {
+ SelectableLazyItemScope(entry.key in state.selectedKeys, isFocused).apply {
+ entry.content.invoke(itemScope)
+ }
}
}
- }
}
}
}
@@ -182,23 +175,24 @@ private fun Modifier.selectable(
selectableState: SelectableLazyListState,
allKeys: List,
itemKey: Any,
-) = this.pointerInput(allKeys, itemKey) {
- awaitPointerEventScope {
- while (true) {
- val event = awaitPointerEvent()
- when (event.type) {
- PointerEventType.Press -> {
- requester?.requestFocus()
- actionHandler.handlePointerEventPress(
- pointerEvent = event,
- keyBindings = keybindings,
- selectableLazyListState = selectableState,
- selectionMode = selectionMode,
- allKeys = allKeys,
- key = itemKey,
- )
+) =
+ this.pointerInput(allKeys, itemKey) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Press -> {
+ requester?.requestFocus()
+ actionHandler.handlePointerEventPress(
+ pointerEvent = event,
+ keyBindings = keybindings,
+ selectableLazyListState = selectableState,
+ selectionMode = selectionMode,
+ allKeys = allKeys,
+ key = itemKey,
+ )
+ }
}
}
}
}
-}
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListScope.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListScope.kt
index dab700a25b..a3a92e1062 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListScope.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListScope.kt
@@ -7,18 +7,17 @@ import org.jetbrains.jewel.foundation.lazy.SelectableLazyListKey.NotSelectable
import org.jetbrains.jewel.foundation.lazy.SelectableLazyListKey.Selectable
/** Interface defining the scope for building a selectable lazy list. */
-interface SelectableLazyListScope {
+public interface SelectableLazyListScope {
/**
* Represents an item in a selectable lazy list.
*
* @param key The unique identifier for the item.
* @param contentType The type of content displayed in the item.
- * @param selectable Determines if the item is selectable. Default is
- * `true`.
+ * @param selectable Determines if the item is selectable. Default is `true`.
* @param content The content of the item as a composable function.
*/
- fun item(
+ public fun item(
key: Any,
contentType: Any? = null,
selectable: Boolean = true,
@@ -29,16 +28,15 @@ interface SelectableLazyListScope {
* Represents a list of items based on the provided parameters.
*
* @param count The number of items in the list.
- * @param key A function that generates a unique key for each item based on
- * its index.
- * @param contentType A function that returns the content type of an item
- * based on its index. Defaults to `null`.
- * @param selectable A function that determines if an item is selectable
- * based on its index. Defaults to `true`.
- * @param itemContent The content of each individual item, specified as a
- * composable function that takes the item's index as a parameter.
+ * @param key A function that generates a unique key for each item based on its index.
+ * @param contentType A function that returns the content type of an item based on its index.
+ * Defaults to `null`.
+ * @param selectable A function that determines if an item is selectable based on its index.
+ * Defaults to `true`.
+ * @param itemContent The content of each individual item, specified as a composable function that
+ * takes the item's index as a parameter.
*/
- fun items(
+ public fun items(
count: Int,
key: (index: Int) -> Any,
contentType: (index: Int) -> Any? = { null },
@@ -52,10 +50,10 @@ interface SelectableLazyListScope {
* @param key The unique identifier for the sticky header.
* @param contentType The type of content in the sticky header.
* @param selectable Specifies whether the sticky header is selectable.
- * @param content The content to be displayed in the sticky header,
- * provided as a composable function
+ * @param content The content to be displayed in the sticky header, provided as a composable
+ * function
*/
- fun stickyHeader(
+ public fun stickyHeader(
key: Any,
contentType: Any? = null,
selectable: Boolean = false,
@@ -70,8 +68,9 @@ internal class SelectableLazyListScopeContainer : SelectableLazyListScope {
private val keys = mutableListOf()
private val entries = mutableListOf()
- fun getEntries() = entries.toList()
- fun getKeys() = keys.toList()
+ public fun getEntries() = entries.toList()
+
+ public fun getKeys() = keys.toList()
internal sealed interface Entry {
data class Item(
@@ -115,13 +114,14 @@ internal class SelectableLazyListScopeContainer : SelectableLazyListScope {
selectable: (index: Int) -> Boolean,
itemContent: @Composable (SelectableLazyItemScope.(index: Int) -> Unit),
) {
- val selectableKeys: List = List(count) {
- if (selectable(it)) {
- Selectable(key(it))
- } else {
- NotSelectable(key(it))
+ val selectableKeys: List =
+ List(count) {
+ if (selectable(it)) {
+ Selectable(key(it))
+ } else {
+ NotSelectable(key(it))
+ }
}
- }
keys.addAll(selectableKeys)
entries.add(Entry.Items(count, key, contentType, itemContent, entriesCount))
entriesCount += count
@@ -140,7 +140,7 @@ internal class SelectableLazyListScopeContainer : SelectableLazyListScope {
}
}
-fun SelectableLazyListScope.items(
+public fun SelectableLazyListScope.items(
items: List,
key: (item: T) -> Any = { it },
contentType: (item: T) -> Any? = { it },
@@ -156,7 +156,7 @@ fun SelectableLazyListScope.items(
)
}
-fun SelectableLazyListScope.itemsIndexed(
+public fun SelectableLazyListScope.itemsIndexed(
items: List,
key: (index: Int, item: T) -> Any = { _, item -> item },
contentType: (index: Int, item: T) -> Any? = { _, item -> item },
@@ -173,11 +173,10 @@ fun SelectableLazyListScope.itemsIndexed(
}
@Composable
-fun LazyItemScope.SelectableLazyItemScope(
+public fun LazyItemScope.SelectableLazyItemScope(
isSelected: Boolean = false,
isActive: Boolean = false,
-): SelectableLazyItemScope =
- SelectableLazyItemScopeDelegate(this, isSelected, isActive)
+): SelectableLazyItemScope = SelectableLazyItemScopeDelegate(this, isSelected, isActive)
internal class SelectableLazyItemScopeDelegate(
private val delegate: LazyItemScope,
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListState.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListState.kt
index 8e54675a17..58c2562ceb 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListState.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/SelectableLazyListState.kt
@@ -3,6 +3,7 @@ package org.jetbrains.jewel.foundation.lazy
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.foundation.lazy.LazyListLayoutInfo
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -12,15 +13,15 @@ import androidx.compose.runtime.setValue
import kotlin.math.max
@Suppress("unused")
-val LazyListState.visibleItemsRange
+public val LazyListState.visibleItemsRange: IntRange
get() = firstVisibleItemIndex..firstVisibleItemIndex + layoutInfo.visibleItemsInfo.size
-val SelectableLazyListState.visibleItemsRange
+public val SelectableLazyListState.visibleItemsRange: IntRange
get() = firstVisibleItemIndex..firstVisibleItemIndex + layoutInfo.visibleItemsInfo.size
-interface SelectableScope {
+public interface SelectableScope {
- var selectedKeys: List
+ public var selectedKeys: List
}
/**
@@ -28,22 +29,21 @@ interface SelectableScope {
*
* @param lazyListState The state object for the underlying lazy list.
*/
-class SelectableLazyListState(
- val lazyListState: LazyListState,
+public class SelectableLazyListState(
+ public val lazyListState: LazyListState,
) : ScrollableState by lazyListState, SelectableScope {
internal var lastKeyEventUsedMouse: Boolean = false
- override var selectedKeys by mutableStateOf(emptyList())
- var lastActiveItemIndex: Int? = null
+ override var selectedKeys: List by mutableStateOf(emptyList())
+ public var lastActiveItemIndex: Int? = null
/**
- *
* @param itemIndex The index of the item to focus on.
* @param animateScroll Whether to animate the scroll to the focused item.
* @param scrollOffset The scroll offset for the focused item.
*/
- suspend fun scrollToItem(
+ public suspend fun scrollToItem(
itemIndex: Int,
animateScroll: Boolean = false,
scrollOffset: Int = 0,
@@ -51,12 +51,12 @@ class SelectableLazyListState(
val visibleRange = visibleItemsRange.drop(2).dropLast(4)
if (itemIndex !in visibleRange && visibleRange.isNotEmpty()) {
when {
- itemIndex < visibleRange.first() -> lazyListState.scrollToItem(
- max(0, itemIndex - 2),
- animateScroll,
- scrollOffset,
- )
-
+ itemIndex < visibleRange.first() ->
+ lazyListState.scrollToItem(
+ max(0, itemIndex - 2),
+ animateScroll,
+ scrollOffset,
+ )
itemIndex > visibleRange.last() -> {
lazyListState.scrollToItem(max(itemIndex - (visibleRange.size + 2), 0), animateScroll, 0)
}
@@ -65,177 +65,191 @@ class SelectableLazyListState(
lastActiveItemIndex = itemIndex
}
- val layoutInfo
+ public val layoutInfo: LazyListLayoutInfo
get() = lazyListState.layoutInfo
/** The index of the first item that is visible */
- val firstVisibleItemIndex: Int
+ public val firstVisibleItemIndex: Int
get() = lazyListState.firstVisibleItemIndex
/**
- * The scroll offset of the first visible item. Scrolling forward is
- * positive - i.e., the amount that the item is offset backwards
+ * The scroll offset of the first visible item. Scrolling forward is positive - i.e., the amount
+ * that the item is offset backwards
*/
@Suppress("unused")
- val firstVisibleItemScrollOffset: Int
+ public val firstVisibleItemScrollOffset: Int
get() = lazyListState.firstVisibleItemScrollOffset
/**
- * [InteractionSource] that will be used to dispatch drag events when
- * this list is being dragged. If you want to know whether the fling (or
- * animated scroll) is in progress, use [isScrollInProgress].
+ * [InteractionSource] that will be used to dispatch drag events when this list is being dragged.
+ * If you want to know whether the fling (or animated scroll) is in progress, use
+ * [isScrollInProgress].
*/
- val interactionSource: InteractionSource
+ public val interactionSource: InteractionSource
get() = lazyListState.interactionSource
// selection handling
-// fun indexOfNextSelectable(currentIndex: Int): Int? {
-// if (currentIndex + 1 > internalKeys.lastIndex) return null
-// for (i in currentIndex + 1..internalKeys.lastIndex) { // todo iterate with instanceOF
-// if (internalKeys[i] is Key.Selectable) return i
-// }
-// return null
-// }
-//
-// fun indexOfPreviousSelectable(currentIndex: Int): Int? {
-// if (currentIndex - 1 < 0) return null
-// for (i in currentIndex - 1 downTo 0) {
-// if (internalKeys[i] is Key.Selectable) return i
-// }
-// return null
-// }
-//
-// /**
-// * Selects a single item at the specified index within the lazy list.
-// *
-// * @param itemIndex The index of the item to select.
-// * @param changeFocus Whether to change the focus to the selected item.
-// * @param skipScroll Whether to skip the scroll to the selected item.
-// */
-// suspend fun selectSingleItem(itemIndex: Int, changeFocus: Boolean = true, skipScroll: Boolean = false) {
-// if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
-// selectedIdsMap.clear()
-// selectedIdsMap[keys[itemIndex]] = itemIndex
-// lastSelectedIndex = itemIndex
-// }
-//
-// /**
-// * Selects a single item with the specified key within the lazy list.
-// *
-// * @param key The key of the item to select.
-// * @param changeFocus Whether to change the focus to the selected item.
-// * @param skipScroll Whether to skip the scroll to the selected item.
-// */
-// suspend fun selectSingleKey(key: Any, changeFocus: Boolean = true, skipScroll: Boolean = false) {
-// val index = internalKeys.indexOfFirst { it.key == key }
-// if (index >= 0 && internalKeys[index] is Key.Selectable) selectSingleItem(index, changeFocus, skipScroll = skipScroll)
-// lastSelectedIndex = index
-// }
-//
-// suspend fun deselectSingleElement(itemIndex: Int, changeFocus: Boolean = true, skipScroll: Boolean = false) {
-// if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
-// selectedIdsMap.remove(keys[itemIndex])
-// }
-//
-// suspend fun toggleSelection(itemIndex: Int, skipScroll: Boolean = false) {
-// if (selectionMode == SelectionMode.None) return
-// selectedIdsMap[keys[itemIndex]]?.let {
-// deselectSingleElement(itemIndex)
-// } ?: if (!isMultiSelectionAllowed) {
-// selectSingleItem(itemIndex, skipScroll = skipScroll)
-// } else {
-// addElementToSelection(itemIndex, skipScroll = skipScroll)
-// }
-// }
-//
-// suspend fun toggleSelectionKey(key: Any, skipScroll: Boolean = false) {
-// if (selectionMode == SelectionMode.None) return
-// val index = internalKeys.indexOfFirst { it.key == key }
-// if (index > 0 && internalKeys[index] is Key.Selectable) toggleSelection(index, skipScroll = skipScroll)
-// lastSelectedIndex = index
-// }
-//
-// suspend fun onExtendSelectionToIndex(itemIndex: Int, changeFocus: Boolean = true, skipScroll: Boolean = false) {
-// if (selectionMode == SelectionMode.None) return
-// if (!isMultiSelectionAllowed) {
-// selectSingleItem(itemIndex, skipScroll = skipScroll)
-// } else {
-// val lastFocussed = lastSelectedIndex ?: itemIndex
-// val indexInterval = if (itemIndex > lastFocussed) {
-// lastFocussed..itemIndex
-// } else {
-// lastFocussed downTo itemIndex
-// }
-// addElementsToSelection(indexInterval.toList())
-// if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
-// }
-// }
-//
-// @Suppress("unused")
-// internal fun addKeyToSelectionMap(keyIndex: Int) {
-// if (selectionMode == SelectionMode.None) return
-// if (internalKeys[keyIndex] is Key.Selectable) {
-// selectedIdsMap[keys[keyIndex]] = keyIndex
-// }
-// }
-//
-// suspend fun addElementToSelection(itemIndex: Int, changeFocus: Boolean = true, skipScroll: Boolean = false) {
-// if (selectionMode == SelectionMode.None) return
-// if (!isMultiSelectionAllowed) {
-// selectSingleItem(itemIndex, false)
-// } else {
-// selectedIdsMap[keys[itemIndex]] = itemIndex
-// }
-// if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
-// lastSelectedIndex = itemIndex
-// }
-//
-// fun deselectAll() {
-// if (selectionMode == SelectionMode.None) return
-// selectedIdsMap.clear()
-// lastSelectedIndex = null
-// }
-//
-// suspend fun addElementsToSelection(itemIndexes: List, itemToFocus: Int? = itemIndexes.lastOrNull()) {
-// if (selectionMode == SelectionMode.None) return
-// if (!isMultiSelectionAllowed) {
-// itemIndexes.lastOrNull()?.let { selectSingleItem(it) }
-// } else {
-// itemIndexes.forEach {
-// selectedIdsMap[keys[it]] = it
-// }
-// itemToFocus?.let { scrollToItem(it) }
-// lastSelectedIndex = itemIndexes.lastOrNull()
-// }
-// }
-//
-// @Suppress("unused")
-// suspend fun removeElementsToSelection(itemIndexes: List, itemToFocus: Int? = itemIndexes.lastOrNull()) {
-// if (selectionMode == SelectionMode.None) return
-// itemIndexes.forEach {
-// selectedIdsMap.remove(keys[it])
-// }
-// itemToFocus?.let { scrollToItem(it) }
-// }
-//
-// @Suppress("Unused")
-// suspend fun toggleElementsToSelection(itemIndexes: List, itemToFocus: Int? = itemIndexes.lastOrNull()) {
-// if (selectionMode == SelectionMode.None) return
-// if (!isMultiSelectionAllowed) {
-// toggleSelection(itemIndexes.last())
-// } else {
-// itemIndexes.forEach { index ->
-// selectedIdsMap[keys[index]]?.let {
-// selectedIdsMap.remove(keys[index])
-// } ?: { selectedIdsMap[keys[index]] = index }
-// }
-// itemToFocus?.let { scrollToItem(it) }
-// lastSelectedIndex = itemIndexes.lastOrNull()
-// }
-// }
+ // fun indexOfNextSelectable(currentIndex: Int): Int? {
+ // if (currentIndex + 1 > internalKeys.lastIndex) return null
+ // for (i in currentIndex + 1..internalKeys.lastIndex) { // todo iterate with instanceOF
+ // if (internalKeys[i] is Key.Selectable) return i
+ // }
+ // return null
+ // }
+ //
+ // fun indexOfPreviousSelectable(currentIndex: Int): Int? {
+ // if (currentIndex - 1 < 0) return null
+ // for (i in currentIndex - 1 downTo 0) {
+ // if (internalKeys[i] is Key.Selectable) return i
+ // }
+ // return null
+ // }
+ //
+ // /**
+ // * Selects a single item at the specified index within the lazy list.
+ // *
+ // * @param itemIndex The index of the item to select.
+ // * @param changeFocus Whether to change the focus to the selected item.
+ // * @param skipScroll Whether to skip the scroll to the selected item.
+ // */
+ // suspend fun selectSingleItem(itemIndex: Int, changeFocus: Boolean = true, skipScroll:
+ // Boolean = false) {
+ // if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
+ // selectedIdsMap.clear()
+ // selectedIdsMap[keys[itemIndex]] = itemIndex
+ // lastSelectedIndex = itemIndex
+ // }
+ //
+ // /**
+ // * Selects a single item with the specified key within the lazy list.
+ // *
+ // * @param key The key of the item to select.
+ // * @param changeFocus Whether to change the focus to the selected item.
+ // * @param skipScroll Whether to skip the scroll to the selected item.
+ // */
+ // suspend fun selectSingleKey(key: Any, changeFocus: Boolean = true, skipScroll: Boolean =
+ // false) {
+ // val index = internalKeys.indexOfFirst { it.key == key }
+ // if (index >= 0 && internalKeys[index] is Key.Selectable) selectSingleItem(index,
+ // changeFocus, skipScroll = skipScroll)
+ // lastSelectedIndex = index
+ // }
+ //
+ // suspend fun deselectSingleElement(itemIndex: Int, changeFocus: Boolean = true, skipScroll:
+ // Boolean = false) {
+ // if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
+ // selectedIdsMap.remove(keys[itemIndex])
+ // }
+ //
+ // suspend fun toggleSelection(itemIndex: Int, skipScroll: Boolean = false) {
+ // if (selectionMode == SelectionMode.None) return
+ // selectedIdsMap[keys[itemIndex]]?.let {
+ // deselectSingleElement(itemIndex)
+ // } ?: if (!isMultiSelectionAllowed) {
+ // selectSingleItem(itemIndex, skipScroll = skipScroll)
+ // } else {
+ // addElementToSelection(itemIndex, skipScroll = skipScroll)
+ // }
+ // }
+ //
+ // suspend fun toggleSelectionKey(key: Any, skipScroll: Boolean = false) {
+ // if (selectionMode == SelectionMode.None) return
+ // val index = internalKeys.indexOfFirst { it.key == key }
+ // if (index > 0 && internalKeys[index] is Key.Selectable) toggleSelection(index,
+ // skipScroll = skipScroll)
+ // lastSelectedIndex = index
+ // }
+ //
+ // suspend fun onExtendSelectionToIndex(itemIndex: Int, changeFocus: Boolean = true,
+ // skipScroll: Boolean = false) {
+ // if (selectionMode == SelectionMode.None) return
+ // if (!isMultiSelectionAllowed) {
+ // selectSingleItem(itemIndex, skipScroll = skipScroll)
+ // } else {
+ // val lastFocussed = lastSelectedIndex ?: itemIndex
+ // val indexInterval = if (itemIndex > lastFocussed) {
+ // lastFocussed..itemIndex
+ // } else {
+ // lastFocussed downTo itemIndex
+ // }
+ // addElementsToSelection(indexInterval.toList())
+ // if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
+ // }
+ // }
+ //
+ // @Suppress("unused")
+ // internal fun addKeyToSelectionMap(keyIndex: Int) {
+ // if (selectionMode == SelectionMode.None) return
+ // if (internalKeys[keyIndex] is Key.Selectable) {
+ // selectedIdsMap[keys[keyIndex]] = keyIndex
+ // }
+ // }
+ //
+ // suspend fun addElementToSelection(itemIndex: Int, changeFocus: Boolean = true, skipScroll:
+ // Boolean = false) {
+ // if (selectionMode == SelectionMode.None) return
+ // if (!isMultiSelectionAllowed) {
+ // selectSingleItem(itemIndex, false)
+ // } else {
+ // selectedIdsMap[keys[itemIndex]] = itemIndex
+ // }
+ // if (changeFocus) scrollToItem(itemIndex, skipScroll = skipScroll)
+ // lastSelectedIndex = itemIndex
+ // }
+ //
+ // fun deselectAll() {
+ // if (selectionMode == SelectionMode.None) return
+ // selectedIdsMap.clear()
+ // lastSelectedIndex = null
+ // }
+ //
+ // suspend fun addElementsToSelection(itemIndexes: List, itemToFocus: Int? =
+ // itemIndexes.lastOrNull()) {
+ // if (selectionMode == SelectionMode.None) return
+ // if (!isMultiSelectionAllowed) {
+ // itemIndexes.lastOrNull()?.let { selectSingleItem(it) }
+ // } else {
+ // itemIndexes.forEach {
+ // selectedIdsMap[keys[it]] = it
+ // }
+ // itemToFocus?.let { scrollToItem(it) }
+ // lastSelectedIndex = itemIndexes.lastOrNull()
+ // }
+ // }
+ //
+ // @Suppress("unused")
+ // suspend fun removeElementsToSelection(itemIndexes: List, itemToFocus: Int? =
+ // itemIndexes.lastOrNull()) {
+ // if (selectionMode == SelectionMode.None) return
+ // itemIndexes.forEach {
+ // selectedIdsMap.remove(keys[it])
+ // }
+ // itemToFocus?.let { scrollToItem(it) }
+ // }
+ //
+ // @Suppress("Unused")
+ // suspend fun toggleElementsToSelection(itemIndexes: List, itemToFocus: Int? =
+ // itemIndexes.lastOrNull()) {
+ // if (selectionMode == SelectionMode.None) return
+ // if (!isMultiSelectionAllowed) {
+ // toggleSelection(itemIndexes.last())
+ // } else {
+ // itemIndexes.forEach { index ->
+ // selectedIdsMap[keys[index]]?.let {
+ // selectedIdsMap.remove(keys[index])
+ // } ?: { selectedIdsMap[keys[index]] = index }
+ // }
+ // itemToFocus?.let { scrollToItem(it) }
+ // lastSelectedIndex = itemIndexes.lastOrNull()
+ // }
+ // }
}
-private suspend fun LazyListState.scrollToItem(index: Int, animate: Boolean, scrollOffset: Int = 0) {
+private suspend fun LazyListState.scrollToItem(
+ index: Int,
+ animate: Boolean,
+ scrollOffset: Int = 0,
+) {
if (animate) {
animateScrollToItem(index, scrollOffset)
} else {
@@ -243,22 +257,18 @@ private suspend fun LazyListState.scrollToItem(index: Int, animate: Boolean, scr
}
}
-/**
- * Represents a selectable key used in a selectable lazy list.
- */
-sealed class SelectableLazyListKey {
+/** Represents a selectable key used in a selectable lazy list. */
+public sealed class SelectableLazyListKey {
- /**
- * The key associated with the item.
- */
- abstract val key: Any
+ /** The key associated with the item. */
+ public abstract val key: Any
/**
* Represents a selectable item key.
*
* @param key The key associated with the item.
*/
- class Selectable(
+ public class Selectable(
override val key: Any,
) : SelectableLazyListKey()
@@ -267,7 +277,7 @@ sealed class SelectableLazyListKey {
*
* @param key The key associated with the item.
*/
- class NotSelectable(
+ public class NotSelectable(
override val key: Any,
) : SelectableLazyListKey()
@@ -283,31 +293,22 @@ sealed class SelectableLazyListKey {
override fun hashCode(): Int = key.hashCode()
}
-interface SelectableLazyItemScope : LazyItemScope {
+public interface SelectableLazyItemScope : LazyItemScope {
- val isSelected: Boolean
- val isActive: Boolean
+ public val isSelected: Boolean
+ public val isActive: Boolean
}
-/**
- * Specifies the selection mode for a selectable lazy list.
- */
-@Suppress("unused")
-enum class SelectionMode {
+/** Specifies the selection mode for a selectable lazy list. */
+public enum class SelectionMode {
- /**
- * No selection is allowed.
- */
+ /** No selection is allowed. */
None,
- /**
- * Only a single item can be selected.
- */
+ /** Only a single item can be selected. */
Single,
- /**
- * Multiple items can be selected.
- */
+ /** Multiple items can be selected. */
Multiple,
}
@@ -319,14 +320,11 @@ enum class SelectionMode {
* @return The remembered state of the selectable lazy list.
*/
@Composable
-fun rememberSelectableLazyListState(
+public fun rememberSelectableLazyListState(
firstVisibleItemIndex: Int = 0,
firstVisibleItemScrollOffset: Int = 0,
-) = remember {
+): SelectableLazyListState = remember {
SelectableLazyListState(
- LazyListState(
- firstVisibleItemIndex,
- firstVisibleItemScrollOffset,
- ),
+ LazyListState(firstVisibleItemIndex, firstVisibleItemScrollOffset),
)
}
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BasicLazyTree.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BasicLazyTree.kt
index b4373449ed..96c38e5bee 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BasicLazyTree.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BasicLazyTree.kt
@@ -48,7 +48,8 @@ import kotlin.time.Duration.Companion.milliseconds
* @param selectionMode The selection mode for the tree nodes.
* @param onElementClick Callback function triggered when a tree node is clicked.
* @param elementBackgroundFocused The background color of a tree node when focused.
- * @param elementBackgroundSelectedFocused The background color of a selected tree node when focused.
+ * @param elementBackgroundSelectedFocused The background color of a selected tree node when
+ * focused.
* @param elementBackgroundSelected The background color of a selected tree node.
* @param indentSize The size of the indent for each level of the tree node.
* @param elementBackgroundCornerSize The corner size of the background shape of a tree node.
@@ -60,18 +61,20 @@ import kotlin.time.Duration.Companion.milliseconds
* @param modifier Optional modifier for styling or positioning the tree view.
* @param onElementDoubleClick Callback function triggered when a tree node is double-clicked.
* @param onSelectionChange Callback function triggered when the selected tree nodes change.
- * @param platformDoubleClickDelay The duration between two consecutive clicks to be considered a double-click.
+ * @param platformDoubleClickDelay The duration between two consecutive clicks to be considered a
+ * double-click.
* @param keyActions The key binding actions for the tree view.
* @param pointerEventScopedActions The pointer event actions for the tree view.
* @param chevronContent The composable function responsible for rendering the chevron icon.
* @param nodeContent The composable function responsible for rendering the content of a tree node.
*
* @suppress("UNCHECKED_CAST")
- * @Composable
+ *
+ * @composable
*/
@Suppress("UNCHECKED_CAST")
@Composable
-fun BasicLazyTree(
+public fun BasicLazyTree(
tree: Tree,
selectionMode: SelectionMode = SelectionMode.Multiple,
onElementClick: (Tree.Element) -> Unit,
@@ -99,7 +102,9 @@ fun BasicLazyTree(
val scope = rememberCoroutineScope()
val flattenedTree =
- remember(tree, treeState.openNodes, treeState.allNodes) { tree.roots.flatMap { it.flattenTree(treeState) } }
+ remember(tree, treeState.openNodes, treeState.allNodes) {
+ tree.roots.flatMap { it.flattenTree(treeState) }
+ }
remember(tree) { // if tree changes we need to update selection changes
onSelectionChange(
@@ -127,16 +132,20 @@ fun BasicLazyTree(
key = { _, item -> item.id },
contentType = { _, item -> item.data },
) { index, element ->
- val elementState = TreeElementState.of(
- active = isActive,
- selected = isSelected,
- expanded = (element as? Tree.Element.Node)?.let { it.id in treeState.openNodes } ?: false,
- )
+ val elementState =
+ TreeElementState.of(
+ active = isActive,
+ selected = isSelected,
+ expanded = (element as? Tree.Element.Node)?.let { it.id in treeState.openNodes } ?: false,
+ )
- val backgroundShape by remember { mutableStateOf(RoundedCornerShape(elementBackgroundCornerSize)) }
+ val backgroundShape by remember {
+ mutableStateOf(RoundedCornerShape(elementBackgroundCornerSize))
+ }
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.defaultMinSize(minHeight = elementMinHeight)
+ modifier =
+ Modifier.defaultMinSize(minHeight = elementMinHeight)
.padding(elementPadding)
.elementBackground(
state = elementState,
@@ -163,7 +172,8 @@ fun BasicLazyTree(
) {
if (element is Tree.Element.Node) {
Box(
- modifier = Modifier.clickable(
+ modifier =
+ Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
) {
@@ -189,7 +199,8 @@ private fun Modifier.elementBackground(
backgroundShape: RoundedCornerShape,
) =
background(
- color = when {
+ color =
+ when {
state.isActive && state.isSelected -> selectedFocused
state.isActive && !state.isSelected -> focused
state.isSelected && !state.isActive -> selected
@@ -200,7 +211,8 @@ private fun Modifier.elementBackground(
@Immutable
@JvmInline
-value class TreeElementState(val state: ULong) : FocusableComponentState, SelectableComponentState {
+public value class TreeElementState(public val state: ULong) :
+ FocusableComponentState, SelectableComponentState {
@Stable
override val isActive: Boolean
@@ -227,14 +239,14 @@ value class TreeElementState(val state: ULong) : FocusableComponentState, Select
get() = state and Selected != 0UL
@Stable
- val isExpanded: Boolean
+ public val isExpanded: Boolean
get() = state and Expanded != 0UL
override fun toString(): String =
"${javaClass.simpleName}(enabled=$isEnabled, focused=$isFocused, expanded=$isExpanded, " +
"pressed=$isPressed, hovered=$isHovered, active=$isActive, selected=$isSelected)"
- fun copy(
+ public fun copy(
enabled: Boolean = isEnabled,
focused: Boolean = isFocused,
expanded: Boolean = isExpanded,
@@ -242,23 +254,24 @@ value class TreeElementState(val state: ULong) : FocusableComponentState, Select
hovered: Boolean = isHovered,
active: Boolean = isActive,
selected: Boolean = isSelected,
- ) = of(
- enabled = enabled,
- focused = focused,
- expanded = expanded,
- pressed = pressed,
- hovered = hovered,
- active = active,
- selected = selected,
- )
+ ): TreeElementState =
+ of(
+ enabled = enabled,
+ focused = focused,
+ expanded = expanded,
+ pressed = pressed,
+ hovered = hovered,
+ active = active,
+ selected = selected,
+ )
- companion object {
+ public companion object {
private const val EXPANDED_BIT_OFFSET = CommonStateBitMask.FIRST_AVAILABLE_OFFSET
private val Expanded = 1UL shl EXPANDED_BIT_OFFSET
- fun of(
+ public fun of(
enabled: Boolean = true,
focused: Boolean = false,
expanded: Boolean = false,
@@ -266,15 +279,16 @@ value class TreeElementState(val state: ULong) : FocusableComponentState, Select
pressed: Boolean = false,
active: Boolean = false,
selected: Boolean = false,
- ) = TreeElementState(
- (if (expanded) Expanded else 0UL) or
- (if (enabled) Enabled else 0UL) or
- (if (focused) Focused else 0UL) or
- (if (pressed) Pressed else 0UL) or
- (if (hovered) Hovered else 0UL) or
- (if (selected) Selected else 0UL) or
- (if (active) Active else 0UL),
- )
+ ): TreeElementState =
+ TreeElementState(
+ (if (expanded) Expanded else 0UL) or
+ (if (enabled) Enabled else 0UL) or
+ (if (focused) Focused else 0UL) or
+ (if (pressed) Pressed else 0UL) or
+ (if (hovered) Hovered else 0UL) or
+ (if (selected) Selected else 0UL) or
+ (if (active) Active else 0UL),
+ )
}
}
@@ -288,19 +302,14 @@ private fun Tree.Element<*>.flattenTree(state: TreeState): MutableList
- orderedChildren.addAll(child.flattenTree(state))
- }
+ children?.forEach { child -> orderedChildren.addAll(child.flattenTree(state)) }
}
-
is Tree.Element.Leaf<*> -> {
orderedChildren.add(this)
}
@@ -309,10 +318,8 @@ private fun Tree.Element<*>.flattenTree(state: TreeState): MutableList.getAllSubNodes(node: Tree.Element.Node<*>) {
- node.children
- ?.filterIsInstance>()
- ?.forEach {
- add(it.id)
- this@getAllSubNodes getAllSubNodes (it)
- }
+ node.children?.filterIsInstance>()?.forEach {
+ add(it.id)
+ this@getAllSubNodes getAllSubNodes (it)
+ }
}
diff --git a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BuildTree.kt b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BuildTree.kt
index 8d88ae786d..7aa98c316a 100644
--- a/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BuildTree.kt
+++ b/foundation/src/main/kotlin/org/jetbrains/jewel/foundation/lazy/tree/BuildTree.kt
@@ -4,22 +4,23 @@ import org.jetbrains.jewel.foundation.GenerateDataFunctions
import java.io.File
import java.nio.file.Path
-fun buildTree(builder: TreeBuilder.() -> Unit): Tree =
+public fun buildTree(builder: TreeBuilder.() -> Unit): Tree =
TreeBuilder().apply(builder).build()
-class TreeBuilder : TreeGeneratorScope {
- sealed class Element {
+public class TreeBuilder : TreeGeneratorScope {
- abstract val id: Any?
+ public sealed class Element {
+
+ public abstract val id: Any?
@GenerateDataFunctions
- class Leaf(val data: T, override val id: Any?) : Element()
+ public class Leaf(public val data: T, override val id: Any?) : Element()
@GenerateDataFunctions
- class Node(
- val data: T,
+ public class Node(
+ public val data: T,
override val id: Any?,
- val childrenGenerator: ChildrenGeneratorScope.() -> Unit,
+ public val childrenGenerator: ChildrenGeneratorScope.() -> Unit,
) : Element()
}
@@ -41,32 +42,34 @@ class TreeBuilder : TreeGeneratorScope {
heads.add(element)
}
- fun build(): Tree {
+ public fun build(): Tree {
val elements = mutableListOf>()
for (index in heads.indices) {
val previous: Tree.Element? = elements.getOrNull(index - 1)?.let { evaluatePrevious(it) }
- val current = when (val elementBuilder = heads[index]) {
- is Element.Leaf -> Tree.Element.Leaf(
- data = elementBuilder.data,
- depth = 0,
- childIndex = index,
- parent = null,
- previous = previous,
- next = null,
- id = elementBuilder.id ?: "$index",
- )
-
- is Element.Node -> Tree.Element.Node(
- data = elementBuilder.data,
- depth = 0,
- childIndex = index,
- parent = null,
- childrenGenerator = { parent -> generateElements(parent, elementBuilder) },
- previous = previous,
- next = null,
- id = elementBuilder.id ?: "$index",
- )
- }
+ val current =
+ when (val elementBuilder = heads[index]) {
+ is Element.Leaf ->
+ Tree.Element.Leaf(
+ data = elementBuilder.data,
+ depth = 0,
+ childIndex = index,
+ parent = null,
+ previous = previous,
+ next = null,
+ id = elementBuilder.id ?: "$index",
+ )
+ is Element.Node ->
+ Tree.Element.Node(
+ data = elementBuilder.data,
+ depth = 0,
+ childIndex = index,
+ parent = null,
+ childrenGenerator = { parent -> generateElements(parent, elementBuilder) },
+ previous = previous,
+ next = null,
+ id = elementBuilder.id ?: "$index",
+ )
+ }
elements.add(current)
previous?.also { it.next = current }
}
@@ -83,61 +86,68 @@ private fun generateElements(
val elements = mutableListOf>()
for (index in childrenGeneratorScope.elements.indices) {
val previous = if (index == 0) parent else elements[index - 1]
- val current = when (val elementBuilder = childrenGeneratorScope.elements[index]) {
- is TreeBuilder.Element.Leaf -> Tree.Element.Leaf(
- data = elementBuilder.data,
- depth = parent.depth + 1,
- childIndex = index,
- parent = parent,
- previous = previous,
- next = null,
- id = elementBuilder.id ?: (parent.id.toString() + "." + index),
- )
-
- is TreeBuilder.Element.Node -> Tree.Element.Node(
- data = elementBuilder.data,
- depth = parent.depth + 1,
- childIndex = index,
- parent = parent,
- childrenGenerator = { generateElements(it, elementBuilder) },
- previous = previous,
- next = null,
- id = elementBuilder.id ?: (parent.id.toString() + "." + index),
- )
- }
+ val current =
+ when (val elementBuilder = childrenGeneratorScope.elements[index]) {
+ is TreeBuilder.Element.Leaf ->
+ Tree.Element.Leaf(
+ data = elementBuilder.data,
+ depth = parent.depth + 1,
+ childIndex = index,
+ parent = parent,
+ previous = previous,
+ next = null,
+ id = elementBuilder.id ?: (parent.id.toString() + "." + index),
+ )
+ is TreeBuilder.Element.Node ->
+ Tree.Element.Node(
+ data = elementBuilder.data,
+ depth = parent.depth + 1,
+ childIndex = index,
+ parent = parent,
+ childrenGenerator = { generateElements(it, elementBuilder) },
+ previous = previous,
+ next = null,
+ id = elementBuilder.id ?: (parent.id.toString() + "." + index),
+ )
+ }
previous.next = current
elements.add(current)
}
return elements
}
-private fun evaluatePrevious(element: Tree.Element): Tree.Element = when (element) {
- is Tree.Element.Leaf -> element
- is Tree.Element.Node -> when (val nephews = element.children) {
- null -> element
- else -> if (nephews.isEmpty()) element else evaluatePrevious(nephews.last())
+private fun evaluatePrevious(element: Tree.Element): Tree.Element =
+ when (element) {
+ is Tree.Element.Leaf -> element
+ is Tree.Element.Node ->
+ when (val nephews = element.children) {
+ null -> element
+ else -> if (nephews.isEmpty()) element else evaluatePrevious(nephews.last())
+ }
}
-}
-interface TreeGeneratorScope {
+public interface TreeGeneratorScope {
- fun addNode(
+ public fun addNode(
data: T,
id: Any? = null,
- childrenGenerator: ChildrenGeneratorScope.() -> Unit = { },
+ childrenGenerator: ChildrenGeneratorScope.() -> Unit = {},
)
- fun addLeaf(data: T, id: Any? = null)
+ public fun addLeaf(data: T, id: Any? = null)
- fun add(element: TreeBuilder.Element)
+ public fun add(element: TreeBuilder.Element)
}
-class ChildrenGeneratorScope(private val parentElement: Tree.Element.Node) : TreeGeneratorScope {
+public class ChildrenGeneratorScope(private val parentElement: Tree.Element.Node) :
+ TreeGeneratorScope {
@GenerateDataFunctions
- class ParentInfo(val data: T, val depth: Int, val index: Int)
+ public class ParentInfo(public val data: T, public val depth: Int, public val index: Int)
- val parent by lazy { ParentInfo(parentElement.data, parentElement.depth, parentElement.childIndex) }
+ public val parent: ParentInfo by lazy {
+ ParentInfo(parentElement.data, parentElement.depth, parentElement.childIndex)
+ }
internal val elements = mutableListOf>()
@@ -158,17 +168,16 @@ class ChildrenGeneratorScope(private val parentElement: Tree.Element.Node)
}
}
-fun Path.asTree(isOpen: (File) -> Boolean = { false }) = toFile().asTree(isOpen)
+public fun Path.asTree(isOpen: (File) -> Boolean = { false }): Tree = toFile().asTree(isOpen)
-fun File.asTree(isOpen: (File) -> Boolean = { false }) = buildTree {
- addNode(this@asTree, isOpen(this@asTree)) {
- generateFileNodes(isOpen)
- }
+public fun File.asTree(isOpen: (File) -> Boolean = { false }): Tree