Browse Source

Merge remote-tracking branch 'origin/master' into support/1.6.0

pull/4382/head
Igor Demin 3 months ago
parent
commit
d8b02e0a23
  1. 80
      CHANGELOG.md
  2. 2
      components/gradle.properties
  3. 5
      components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/FontRes.kt
  4. 1
      components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/ImagesRes.kt
  5. 1
      components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/StringRes.kt
  6. 35
      components/resources/library/src/blockingMain/kotlin/org/jetbrains/compose/resources/ResourceState.blocking.kt
  7. 29
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceState.kt
  8. 2
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt
  9. 23
      components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.kt
  10. 2
      components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt
  11. 37
      components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt
  12. 3
      compose/integrations/composable-test-cases/buildSrc/src/main/kotlin/TargetsConfiguration.kt
  13. 1
      compose/integrations/composable-test-cases/common/build.gradle.kts
  14. 8
      compose/integrations/composable-test-cases/common/src/commonMain/kotlin/com/example/common/Applier.kt
  15. 8
      compose/integrations/composable-test-cases/gradle.properties
  16. 2
      compose/integrations/composable-test-cases/testcases/expectActual/lib/src/wasmJsMain/kotlin/Dependencies.wasmJs.kt
  17. 38
      compose/integrations/composable-test-cases/testcases/inheritance/composableInterface/main/src/commonTest/kotlin/CollectionOfComposablesTests.kt
  18. 2
      examples/chat/README.md
  19. 1
      examples/chat/shared/build.gradle.kts
  20. 3
      examples/chat/shared/src/androidMain/kotlin/currentTime.android.kt
  21. 3
      examples/chat/shared/src/commonMain/kotlin/ChatApp.kt
  22. 2
      examples/chat/shared/src/commonMain/kotlin/ChatMessage.kt
  23. 8
      examples/chat/shared/src/commonMain/kotlin/Data.kt
  24. 18
      examples/chat/shared/src/commonMain/kotlin/currentTime.common.kt
  25. 3
      examples/chat/shared/src/desktopMain/kotlin/currentTime.desktop.kt
  26. 6
      examples/chat/shared/src/iosMain/kotlin/currentTime.ios.kt
  27. 2
      examples/chat/shared/src/iosMain/kotlin/main.ios.kt
  28. 5
      examples/chat/shared/src/jsMain/kotlin/currentTime.js.kt
  29. 6
      examples/chat/shared/src/macosMain/kotlin/currentTime.macos.kt
  30. 2
      examples/cocoapods-ios-example/README.md
  31. 2
      examples/codeviewer/README.md
  32. 2
      examples/graphics-2d/README.md
  33. 2
      examples/imageviewer/README.md
  34. 2
      examples/todoapp-lite/README.md
  35. 2
      examples/widgets-gallery/README.md
  36. 3
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerCompatibility.kt
  37. 17
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt
  38. 1
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/PlatformSettings.kt
  39. 1
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt
  40. 16
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt
  41. 6
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt
  42. 2
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt
  43. 37
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt
  44. 164
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesSpec.kt
  45. 140
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt
  46. 2
      gradle-plugins/compose/src/test/test-projects/application/macOptions/Expected-Info.plist
  47. 1
      gradle-plugins/compose/src/test/test-projects/application/macOptions/build.gradle
  48. 64
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Drawable0.kt
  49. 21
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Font0.kt
  50. 165
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Res.kt
  51. 98
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/String0.kt
  52. 3
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/src/commonMain/kotlin/App.kt
  53. 6
      gradle-plugins/compose/src/test/test-projects/misc/emptyResources/expected/Res.kt
  54. 50
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/build.gradle.kts
  55. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable0.kt
  56. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable1.kt
  57. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable10.kt
  58. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable11.kt
  59. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable12.kt
  60. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable13.kt
  61. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable14.kt
  62. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable15.kt
  63. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable16.kt
  64. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable17.kt
  65. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable18.kt
  66. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable19.kt
  67. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable2.kt
  68. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable20.kt
  69. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable21.kt
  70. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable22.kt
  71. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable23.kt
  72. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable24.kt
  73. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable25.kt
  74. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable26.kt
  75. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable27.kt
  76. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable28.kt
  77. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable29.kt
  78. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable3.kt
  79. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable30.kt
  80. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable31.kt
  81. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable32.kt
  82. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable33.kt
  83. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable34.kt
  84. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable35.kt
  85. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable36.kt
  86. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable37.kt
  87. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable38.kt
  88. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable39.kt
  89. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable4.kt
  90. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable40.kt
  91. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable41.kt
  92. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable42.kt
  93. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable43.kt
  94. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable44.kt
  95. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable45.kt
  96. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable46.kt
  97. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable47.kt
  98. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable48.kt
  99. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable49.kt
  100. 5510
      gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable5.kt
  101. Some files were not shown because too many files have changed in this diff Show More

80
CHANGELOG.md

@ -1,7 +1,85 @@
# 1.6.0-rc01 (February 2024)
# 1.6.0-rc03 (February 2024)
_Changes since 1.6.0-rc02_
- **[Support Kotlin 2.0.0-Beta4](https://github.com/JetBrains/compose-multiplatform/pull/4332)** <sub>Common</sub>
- _(prerelease fix)_ [Resources. Don't return a cached value when pass new args](https://github.com/JetBrains/compose-multiplatform/pull/4333) <sub>Common</sub>
- _(prerelease fix)_ [Fix crash "ComposeUIViewController.view should be attached to window](https://github.com/JetBrains/compose-multiplatform-core/pull/1110) <sub>iOS</sub>
- _(prerelease fix)_ [Fix App crashes when Compose SwfitUI View container removed from hierarchy](https://github.com/JetBrains/compose-multiplatform-core/pull/1114) <sub>iOS</sub>
- _(prerelease fix)_ [Fix Keyboard disappears on IME action](https://github.com/JetBrains/compose-multiplatform-core/pull/1118) <sub>iOS</sub>
- _(prerelease fix)_ [Fix `SelectionContainer` occasionally crashes on iOS](https://github.com/JetBrains/compose-multiplatform-core/pull/1121) <sub>iOS</sub>
- _(prerelease fix)_ [Fix crash after open/close dropdown on iOS](https://github.com/JetBrains/compose-multiplatform-core/pull/1127) <sub>iOS</sub>
- _(prerelease fix)_ [Fix Password popup disappears only on the second focused TextField](https://github.com/JetBrains/compose-multiplatform-core/pull/1128) <sub>iOS</sub>
- _(prerelease fix)_ [Fix mouse input above SwingPanel](https://github.com/JetBrains/compose-multiplatform-core/pull/1119) <sub>Desktop</sub>
- _(prerelease fix)_ [Fix non working accessibility on Desktop](https://github.com/JetBrains/compose-multiplatform-core/pull/1129) <sub>Desktop</sub>
- _(prerelease fix)_ [Resources. Fix import of Android flavors](https://github.com/JetBrains/compose-multiplatform/pull/4319) <sub>Android</sub>
## Dependencies
This version of Compose Multiplatform is based on the next Jetpack Compose libraries:
- [Compiler 1.5.8](https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.8)
- [Runtime 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-runtime#1.6.1)
- [UI 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.1)
- [Foundation 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.6.1)
- [Material 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-material#1.6.1)
- [Material3 1.2.0](https://developer.android.com/jetpack/androidx/releases/compose-material3#1.2.0)
# 1.6.0-rc02 (February 2024)
_Changes since 1.6.0-rc01_
## Breaking changes
_since 1.6.0-beta01_
- When the resource library is used, resources should be imported explicitly
> To quickly change your code to a new state, replace this:
> ```
> import <modulePackage>.generated.resources.Res
> ```
> by this:
> ```
> import <modulePackage>.generated.resources.*
> ```
> and perform "Code - Optimize Imports" to apply the project code style. If code style doesn't allow wildcrads, `import <modulePackage>.generated.resources.*` will be replaced by explicit imports.
## iOS/desktop/web
### Fixes
- _(prerelease fix)_ [Fix "AlertDialog doesn't work with ProvidableCompositionLocal"](https://github.com/JetBrains/compose-multiplatform-core/pull/1104)
## Resource library
### Fixes
- _(prerelease fix)_ [Fix resource accessors compilation when there are huge number of resource files](https://github.com/JetBrains/compose-multiplatform/pull/4294)
## Dependencies
This version of Compose Multiplatform is based on the next Jetpack Compose libraries:
- [Compiler 1.5.8](https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.8)
- [Runtime 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-runtime#1.6.1)
- [UI 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.1)
- [Foundation 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.6.1)
- [Material 1.6.1](https://developer.android.com/jetpack/androidx/releases/compose-material#1.6.1)
- [Material3 1.2.0](https://developer.android.com/jetpack/androidx/releases/compose-material3#1.2.0)
# 1.6.0-rc01 (February 2024)
_Changes since 1.6.0-beta02_
## Known issues and solutions
### Could not find "org.jetbrains.compose.annotation-internal:annotation"
_(or org.jetbrains.compose.collection-internal:collection)_, _[link](https://github.com/JetBrains/compose-multiplatform/issues/4277)_
It happens because some library depends on `1.6.0-beta02` which isn't binary compatible with `1.6.0-rc01`.
To find this library, call `./gradlew shared:dependencies` (replace `shared` by your main module). Downgrade this library or ask the library author to upgrade it to `1.6.0-rc01`.
### Could not find androidx.annotation:annotation:...
_(or org.jetbrains.compose.collection-internal:collection)_
It happens because `1.6.0` depends on [collection](https://developer.android.com/jetpack/androidx/releases/collection) and [annnotation](https://developer.android.com/jetpack/androidx/releases/annotation) libraries that are available only in the Google Maven repository.
To solve this, add `google()` maven repository to `build.gradle.kts`:
```
repositories {
...
google()
}
```
## iOS/desktop/web
### Fixes

2
components/gradle.properties

@ -8,7 +8,7 @@ android.useAndroidX=true
#Versions
kotlin.version=1.9.22
compose.version=1.6.0-dev1397
compose.version=1.6.0-rc01
agp.version=8.1.2
#Compose

5
components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/FontRes.kt

@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import components.resources.demo.shared.generated.resources.Res
import components.resources.demo.shared.generated.resources.*
import org.jetbrains.compose.resources.Font
@Composable
@ -31,7 +32,7 @@ fun FontRes(paddingValues: PaddingValues) {
text = """
Text(
modifier = Modifier.padding(16.dp),
fontFamily = FontFamily(Font(Res.font.workbench_regular)),
fontFamily = FontFamily(Font(Res.font.Workbench_Regular)),
style = MaterialTheme.typography.headlineLarge,
text = "brown fox jumps over the lazy dog"
)
@ -71,7 +72,7 @@ fun FontRes(paddingValues: PaddingValues) {
modifier = Modifier.padding(16.dp),
fontFamily = FontFamily(Font(Res.font.font_awesome)),
style = MaterialTheme.typography.headlineLarge,
text ="\uf1ba \uf238 \uf21a \uf1bb \uf1b8 \uf09b \uf269 \uf1d0 \uf15a \uf293 \uf1c6"
text = "\uf1ba \uf238 \uf21a \uf1bb \uf1b8 \uf09b \uf269 \uf1d0 \uf15a \uf293 \uf1c6"
)
}
}

1
components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/ImagesRes.kt

@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import components.resources.demo.shared.generated.resources.Res
import components.resources.demo.shared.generated.resources.*
import org.jetbrains.compose.resources.imageResource
import org.jetbrains.compose.resources.vectorResource
import org.jetbrains.compose.resources.painterResource

1
components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/StringRes.kt

@ -9,6 +9,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import components.resources.demo.shared.generated.resources.Res
import components.resources.demo.shared.generated.resources.*
import org.jetbrains.compose.resources.stringArrayResource
import org.jetbrains.compose.resources.stringResource

35
components/resources/library/src/blockingMain/kotlin/org/jetbrains/compose/resources/ResourceState.blocking.kt

@ -5,12 +5,43 @@ import kotlinx.coroutines.runBlocking
@Composable
internal actual fun <T> rememberResourceState(
key: Any,
key1: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key, environment) {
return remember(key1, environment) {
mutableStateOf(
runBlocking { block(environment) }
)
}
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key1, key2, environment) {
mutableStateOf(
runBlocking { block(environment) }
)
}
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
key3: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key1, key2, key3, environment) {
mutableStateOf(
runBlocking { block(environment) }
)

29
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceState.kt

@ -10,7 +10,34 @@ import androidx.compose.runtime.State
*/
@Composable
internal expect fun <T> rememberResourceState(
key: Any,
key1: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T>
/**
* This is a platform-specific function that calculates and remembers a state.
* For all platforms except a JS it is a blocking function.
* On the JS platform it loads the state asynchronously and uses `getDefault` as an initial state value.
*/
@Composable
internal expect fun <T> rememberResourceState(
key1: Any,
key2: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T>
/**
* This is a platform-specific function that calculates and remembers a state.
* For all platforms except a JS it is a blocking function.
* On the JS platform it loads the state asynchronously and uses `getDefault` as an initial state value.
*/
@Composable
internal expect fun <T> rememberResourceState(
key1: Any,
key2: Any,
key3: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T>

2
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt

@ -123,7 +123,7 @@ private suspend fun loadString(
fun stringResource(resource: StringResource, vararg formatArgs: Any): String {
val resourceReader = LocalResourceReader.current
val args = formatArgs.map { it.toString() }
val str by rememberResourceState(resource, { "" }) { env ->
val str by rememberResourceState(resource, args, { "" }) { env ->
loadString(resource, args, resourceReader, env)
}
return str

23
components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.kt

@ -111,6 +111,29 @@ class ComposeResourceTest {
assertEquals(listOf("item 1", "item 2", "item 3"), str_arr)
}
// https://github.com/JetBrains/compose-multiplatform/issues/4325
@Test
fun testReadStringFromDifferentArgs() = runComposeUiTest {
var arg by mutableStateOf(42)
var str1 = ""
var str2 = ""
setContent {
CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
str1 = stringResource(TestStringResource("str_template"), "test1", arg)
str2 = stringResource(TestStringResource("str_template"), "test2", arg)
}
}
waitForIdle()
assertEquals("Hello, test1! You have 42 new messages.", str1)
assertEquals("Hello, test2! You have 42 new messages.", str2)
arg = 31415
waitForIdle()
assertEquals("Hello, test1! You have 31415 new messages.", str1)
assertEquals("Hello, test2! You have 31415 new messages.", str2)
}
@Test
fun testLoadStringResource() = runTest {
assertEquals("Compose Resources App", getString(TestStringResource("app_name")))

2
components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt

@ -33,7 +33,7 @@ private val defaultEmptyFont by lazy { Font("org.jetbrains.compose.emptyFont", B
@Composable
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font {
val resourceReader = LocalResourceReader.current
val fontFile by rememberResourceState(resource, { defaultEmptyFont }) { env ->
val fontFile by rememberResourceState(resource, weight, style, { defaultEmptyFont }) { env ->
val path = resource.getPathByEnvironment(env)
val fontBytes = resourceReader.read(path)
Font(path, fontBytes, weight, style)

37
components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt

@ -8,13 +8,44 @@ import androidx.compose.runtime.remember
@Composable
internal actual fun <T> rememberResourceState(
key: Any,
key1: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key) { mutableStateOf(getDefault()) }
LaunchedEffect(key) {
val state = remember(key1) { mutableStateOf(getDefault()) }
LaunchedEffect(key1) {
state.value = block(environment)
}
return state
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1, key2) { mutableStateOf(getDefault()) }
LaunchedEffect(key1, key2) {
state.value = block(environment)
}
return state
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
key3: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1, key2, key3) { mutableStateOf(getDefault()) }
LaunchedEffect(key1, key2, key3) {
state.value = block(environment)
}
return state

3
compose/integrations/composable-test-cases/buildSrc/src/main/kotlin/TargetsConfiguration.kt

@ -26,6 +26,9 @@ val Project.isMingwX64Enabled: Boolean
fun KotlinMultiplatformExtension.configureTargets() {
jvm("desktop")
configureJsTargets()
wasmJs {
d8 {}
}
ios()
iosArm64()
iosSimulatorArm64()

1
compose/integrations/composable-test-cases/common/build.gradle.kts

@ -13,7 +13,6 @@ kotlin {
val commonMain by getting {
dependencies {
implementation(compose.runtime)
implementation(libs.kotlinx.datetime)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
}
}

8
compose/integrations/composable-test-cases/common/src/commonMain/kotlin/com/example/common/Applier.kt

@ -9,7 +9,6 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.ObserverHandle
import androidx.compose.runtime.snapshots.Snapshot
import kotlinx.coroutines.*
import kotlinx.datetime.Clock
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@ -76,11 +75,14 @@ private object GlobalSnapshotManager {
private class MonotonicClockImpl : MonotonicFrameClock {
private val NANOS_PER_MILLI = 1_000_000
private var i = 0L
override suspend fun <R> withFrameNanos(
onFrame: (Long) -> R
): R = suspendCoroutine { continuation ->
val now = Clock.System.now()
val currentNanos = now.toEpochMilliseconds() * NANOS_PER_MILLI + now.nanosecondsOfSecond
val now = ++i
val currentNanos = now * NANOS_PER_MILLI
val result = onFrame(currentNanos)
continuation.resume(result)
}

8
compose/integrations/composable-test-cases/gradle.properties

@ -1,15 +1,15 @@
org.gradle.jvmargs=-Xmx2048M -XX:MaxMetaspaceSize=512m
org.gradle.jvmargs=-Xmx4096M -XX:MaxMetaspaceSize=512m
kotlin.code.style=official
android.useAndroidX=true
kotlin.version=1.9.22
agp.version=7.3.0
compose.version=1.6.0-dev1357
compose.version=1.6.0-rc03
kotlinx.coroutines.version=1.8.0-RC
kotlinx.coroutines.version=1.8.0
#empty by default - a default version will be used
#compose.kotlinCompilerPluginVersion=23.12.18
compose.kotlinCompilerPluginVersion=1.5.8-beta01
compose.kotlinCompilerPluginVersion=1.5.8.1
# default|failingJs - see enum class CasesToRun
tests.casesToRun=default

2
compose/integrations/composable-test-cases/testcases/expectActual/lib/src/wasmJsMain/kotlin/Dependencies.wasmJs.kt

@ -0,0 +1,2 @@
actual val Abc.composableIntVal: Int
get() = 100

38
compose/integrations/composable-test-cases/testcases/inheritance/composableInterface/main/src/commonTest/kotlin/CollectionOfComposablesTests.kt

@ -86,6 +86,44 @@ class CollectionOfComposablesTests {
actual = root.dump()
)
}
/** Composable lambdas nested more than single level deep in generic types provoke compilation failure for K/Native
* https://github.com/JetBrains/compose-multiplatform/issues/3466
*/
@Test
fun testNestedComposableTypes() = runTest {
data class Container<T>(val value: T)
class DoubleNested(
val f: List<Container<@Composable () -> Unit>>,
)
class SingleNested(
val f: List<@Composable () -> Unit>,
)
val composables: List<@Composable () -> Unit> = listOf(@Composable { TextLeafNode("a") }, @Composable { TextLeafNode("b") })
val single = SingleNested(composables)
val singleRoot = composeText {
for (c in single.f) {
c()
}
}
val double = DoubleNested(composables.map { Container(it) })
val doubleRoot = composeText {
for (c in double.f.map { it.value }) {
c()
}
}
assertEquals(
expected = "root:{a, b}",
actual = singleRoot.dump()
)
assertEquals(singleRoot.dump(), doubleRoot.dump())
}
}

2
examples/chat/README.md

@ -9,7 +9,7 @@ Example can run on Android, iOS, desktop or in a browser.
## Setting up your development environment
To setup the environment, please consult these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
To setup the environment, please consult these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

1
examples/chat/shared/build.gradle.kts

@ -53,6 +53,7 @@ kotlin {
implementation(compose.material)
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
implementation(compose.components.resources)
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
}
}
val androidMain by getting {

3
examples/chat/shared/src/androidMain/kotlin/currentTime.android.kt

@ -1,3 +0,0 @@
actual fun timestampMs(): Long {
return System.currentTimeMillis()
}

3
examples/chat/shared/src/commonMain/kotlin/ChatApp.kt

@ -77,7 +77,7 @@ fun ChatApp(displayTextField: Boolean = true) {
SendMessage { text ->
store.send(
Action.SendMessage(
Message(myUser, timeMs = timestampMs(), text)
Message(myUser, text)
)
)
}
@ -100,7 +100,6 @@ fun ChatApp(displayTextField: Boolean = true) {
Action.SendMessage(
message = Message(
user = thisFriend,
timeMs = timestampMs(),
text = thisMessage
)
)

2
examples/chat/shared/src/commonMain/kotlin/ChatMessage.kt

@ -96,7 +96,7 @@ inline fun ChatMessage(isMyMessage: Boolean, message: Message) {
modifier = Modifier.align(Alignment.End)
) {
Text(
text = timeToString(message.timeMs),
text = timeToString(message.seconds),
textAlign = TextAlign.End,
style = MaterialTheme.typography.subtitle1.copy(fontSize = 10.sp),
color = ChatColors.TIME_TEXT

8
examples/chat/shared/src/commonMain/kotlin/Data.kt

@ -1,21 +1,21 @@
import androidx.compose.ui.graphics.Color
import kotlinx.datetime.Clock
import kotlin.random.Random
import kotlin.random.nextInt
data class Message private constructor(
data class Message(
val user: User,
val timeMs: Long,
val text: String,
val seconds: Long,
val id: Long
) {
constructor(
user: User,
timeMs: Long,
text: String
) : this(
user = user,
timeMs = timeMs,
text = text,
seconds = Clock.System.now().epochSeconds,
id = Random.nextLong()
)
}

18
examples/chat/shared/src/commonMain/kotlin/currentTime.common.kt

@ -1,10 +1,15 @@
fun timeToString(timestampMs: Long): String {
val seconds = timestampMs
val minutes = seconds / 1000 / 60
val hours = minutes / 24
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
val m = minutes % 60
val h = hours % 24
fun timeToString(seconds: Long): String {
val instant: Instant = Instant.fromEpochSeconds(seconds)
val localTime = instant.toLocalDateTime(TimeZone.currentSystemDefault())
val m = localTime.minute
val h = localTime.hour
val mm = if (m < 10) {
"0$m"
@ -19,4 +24,3 @@ fun timeToString(timestampMs: Long): String {
return "$hh:$mm"
}
expect fun timestampMs(): Long

3
examples/chat/shared/src/desktopMain/kotlin/currentTime.desktop.kt

@ -1,3 +0,0 @@
actual fun timestampMs(): Long {
return System.currentTimeMillis()
}

6
examples/chat/shared/src/iosMain/kotlin/currentTime.ios.kt

@ -1,6 +0,0 @@
import platform.Foundation.NSDate
import platform.Foundation.timeIntervalSince1970
actual fun timestampMs(): Long {
return (NSDate().timeIntervalSince1970() * 1000).toLong()
}

2
examples/chat/shared/src/iosMain/kotlin/main.ios.kt

@ -6,7 +6,7 @@ fun ChatViewController(): UIViewController = ComposeUIViewController {
}
fun sendMessage(text: String) {
store.send(Action.SendMessage(Message(myUser, timestampMs(), text)))
store.send(Action.SendMessage(Message(myUser, text)))
}
fun gradient3Colors() = ChatColors.GRADIENT_3

5
examples/chat/shared/src/jsMain/kotlin/currentTime.js.kt

@ -1,5 +0,0 @@
import kotlin.js.Date
actual fun timestampMs(): Long {
return Date.now().toLong()
}

6
examples/chat/shared/src/macosMain/kotlin/currentTime.macos.kt

@ -1,6 +0,0 @@
import platform.Foundation.NSDate
import platform.Foundation.timeIntervalSince1970
actual fun timestampMs(): Long {
return NSDate().timeIntervalSince1970().toLong()
}

2
examples/cocoapods-ios-example/README.md

@ -10,7 +10,7 @@ The official Kotlin documentation provides more information on working with Coco
## Setting up your development environment
To setup the environment, please consult these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
To setup the environment, please consult these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

2
examples/codeviewer/README.md

@ -3,7 +3,7 @@ Code Viewer example for Desktop, Android and iOS written in Compose Multiplatfor
## Setting up your development environment
To setup the environment, please consult these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
To setup the environment, please consult these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

2
examples/graphics-2d/README.md

@ -5,7 +5,7 @@ Example can run on Android, iOS, desktop or in a browser.
## Setting up your development environment
To setup the environment, please consult
these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

2
examples/imageviewer/README.md

@ -5,7 +5,7 @@ based on Compose Multiplatform (desktop, Android and iOS).
## Setting up your development environment
To setup the environment, please consult these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
To setup the environment, please consult these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

2
examples/todoapp-lite/README.md

@ -6,7 +6,7 @@ Supported targets: Android, Desktop and iOS.
## Setting up your development environment
To setup the environment, please consult these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
To setup the environment, please consult these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

2
examples/widgets-gallery/README.md

@ -10,7 +10,7 @@ demonstrating how to use various Material widgets.
## Setting up your development environment
To setup the environment, please consult these [instructions](https://github.com/JetBrains/compose-multiplatform-template#setting-up-your-development-environment).
To setup the environment, please consult these [instructions](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html).
## How to run

3
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerCompatibility.kt

@ -22,8 +22,9 @@ internal object ComposeCompilerCompatibility {
"1.9.20-RC2" to "1.5.3-rc01",
"1.9.20" to "1.5.3",
"1.9.21" to "1.5.4",
"1.9.22" to "1.5.8.1-beta02",
"1.9.22" to "1.5.8.1",
"2.0.0-Beta1" to "1.5.4-dev1-kt2.0.0-Beta1",
"2.0.0-Beta4" to "1.5.9-kt-2.0.0-Beta4",
)
fun compilerVersionFor(kotlinVersion: String): String {

17
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt

@ -87,23 +87,6 @@ abstract class ComposePlugin : Plugin<Project> {
disableSignatureClashCheck(project)
}
// TODO: remove this (https://youtrack.jetbrains.com/issue/COMPOSE-939)
// we substitute the coroutines version for web targets in user projects,
// so they don't need to do that manually
project.configurations.all {
val isWeb = it.name.startsWith("wasmJs") || it.name.startsWith("js")
if (isWeb) {
it.resolutionStrategy.eachDependency {
if (it.requested.group.startsWith("org.jetbrains.kotlinx") &&
it.requested.name.startsWith("kotlinx-coroutines-")) {
if (it.requested.version?.startsWith("1.7") == true) {
it.useVersion("1.8.0-RC2")
}
}
}
}
}
}
private fun disableSignatureClashCheck(project: Project) {

1
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/PlatformSettings.kt

@ -26,6 +26,7 @@ abstract class AbstractMacOSPlatformSettings : AbstractPlatformSettings() {
var dmgPackageVersion: String? = null
var dmgPackageBuildVersion: String? = null
var appCategory: String? = null
var minimumSystemVersion: String? = null
/**

1
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt

@ -393,6 +393,7 @@ internal fun JvmApplicationContext.configurePlatformSettings(
)
packageTask.macAppStore.set(mac.appStore)
packageTask.macAppCategory.set(mac.appCategory)
packageTask.macMinimumSystemVersion.set(mac.minimumSystemVersion)
val defaultEntitlements = defaultResources.get { defaultEntitlements }
packageTask.macEntitlementsFile.set(mac.entitlementsFile.orElse(defaultEntitlements))
packageTask.macRuntimeEntitlementsFile.set(mac.runtimeEntitlementsFile.orElse(defaultEntitlements))

16
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt

@ -162,6 +162,10 @@ abstract class AbstractJPackageTask @Inject constructor(
@get:Optional
val macAppCategory: Property<String?> = objects.nullableProperty()
@get:Input
@get:Optional
val macMinimumSystemVersion: Property<String?> = objects.nullableProperty()
@get:InputFile
@get:Optional
@get:PathSensitive(PathSensitivity.ABSOLUTE)
@ -499,11 +503,12 @@ abstract class AbstractJPackageTask @Inject constructor(
.writeToFile(jpackageResources.ioFile.resolve("Info.plist"))
if (macAppStore.orNull == true) {
val systemVersion = macMinimumSystemVersion.orNull ?: "10.13"
val productDefPlistXml = """
<key>os</key>
<array>
<string>10.13</string>
</array>
<key>os</key>
<array>
<string>$systemVersion</string>
</array>
""".trimIndent()
InfoPlistBuilder(productDefPlistXml)
.writeToFile(jpackageResources.ioFile.resolve("product-def.plist"))
@ -581,7 +586,8 @@ abstract class AbstractJPackageTask @Inject constructor(
private fun setInfoPlistValues(plist: InfoPlistBuilder) {
check(currentOS == OS.MacOS) { "Current OS is not macOS: $currentOS" }
plist[PlistKeys.LSMinimumSystemVersion] = "10.13"
val systemVersion = macMinimumSystemVersion.orNull ?: "10.13"
plist[PlistKeys.LSMinimumSystemVersion] = systemVersion
plist[PlistKeys.CFBundleDevelopmentRegion] = "English"
plist[PlistKeys.CFBundleAllowMixedLocalizations] = "true"
val packageName = packageName.get()

6
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractNativeMacApplicationPackageAppDirTask.kt

@ -39,6 +39,10 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac
@get:Optional
val copyright: Property<String?> = objects.nullableProperty()
@get:Input
@get:Optional
val minimumSystemVersion: Property<String?> = objects.nullableProperty()
override fun createPackage(destinationDir: File, workingDir: File) {
val packageName = packageName.get()
val appDir = destinationDir.resolve("$packageName.app").apply { mkdirs() }
@ -60,7 +64,7 @@ abstract class AbstractNativeMacApplicationPackageAppDirTask : AbstractNativeMac
}
private fun InfoPlistBuilder.setupInfoPlist(executableName: String) {
this[PlistKeys.LSMinimumSystemVersion] = KOTLIN_NATIVE_MIN_SUPPORTED_MAC_OS
this[PlistKeys.LSMinimumSystemVersion] = minimumSystemVersion.getOrElse(KOTLIN_NATIVE_MIN_SUPPORTED_MAC_OS)
this[PlistKeys.CFBundleDevelopmentRegion] = "English"
this[PlistKeys.CFBundleAllowMixedLocalizations] = "true"
this[PlistKeys.CFBundleExecutable] = executableName

2
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt

@ -56,7 +56,7 @@ internal abstract class GenerateResClassTask : DefaultTask() {
}
.groupBy { it.type }
.mapValues { (_, items) -> items.groupBy { it.name } }
getResFileSpec(resources, packageName.get()).writeTo(kotlinDir)
getResFileSpecs(resources, packageName.get()).forEach { it.writeTo(kotlinDir) }
} else {
logger.info("Generation Res class is disabled")
}

37
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt

@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.utils.ObservableSet
import java.io.File
@ -75,22 +76,25 @@ private fun Project.configureAndroidComposeResources(
val commonResourcesDir = projectDir.resolve("src/${KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME}/$COMPOSE_RESOURCES_DIR")
//Copy common compose resources except fonts to android resources
val copyCommonAndroidComposeResources = registerTask<CopyCommonAndroidComposeResources>(
val commonAndroidComposeResourcesDir = layout.buildDirectory.dir("$RES_GEN_DIR/commonAndroidComposeResources")
val copyCommonAndroidComposeResources = registerTask<Copy>(
"copyCommonAndroidComposeResources"
) {
from.set(commonResourcesDir)
outputDirectory.set(layout.buildDirectory.dir("$RES_GEN_DIR/commonAndroidComposeResources"))
includeEmptyDirs = false
from(commonResourcesDir)
exclude("**/font*/*")
into(commonAndroidComposeResourcesDir)
}
tasks.configureEachWithType<ProcessJavaResTask> { dependsOn(copyCommonAndroidComposeResources) }
//mark all composeResources as Android resources
kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).all { androidTarget ->
androidTarget.compilations.all { compilation: KotlinCompilation<*> ->
androidTarget.compilations.all { compilation: KotlinJvmAndroidCompilation ->
compilation.defaultSourceSet.androidSourceSetInfoOrNull?.let { kotlinAndroidSourceSet ->
androidExtension.sourceSets
.matching { it.name == kotlinAndroidSourceSet.androidSourceSetName }
.all { androidSourceSet ->
androidSourceSet.resources.srcDir(copyCommonAndroidComposeResources.flatMap { it.outputDirectory.asFile })
compilation.androidVariant.processJavaResourcesProvider.dependsOn(copyCommonAndroidComposeResources)
androidSourceSet.resources.srcDir(commonAndroidComposeResourcesDir)
(compilation.allKotlinSourceSets as? ObservableSet<KotlinSourceSet>)?.forAll { kotlinSourceSet ->
if (kotlinSourceSet.name != KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
androidSourceSet.resources.srcDir(
@ -166,27 +170,6 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File,
}
}
internal abstract class CopyCommonAndroidComposeResources : DefaultTask() {
@get:Inject
abstract val fileSystem: FileSystemOperations
@get:InputFiles
abstract val from: Property<File>
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty
@TaskAction
fun action() {
fileSystem.copy {
it.includeEmptyDirs = false
it.from(from)
it.exclude("**/font*/*")
it.into(outputDirectory)
}
}
}
//Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API
internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() {
@get:Inject

164
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesSpec.kt

@ -1,9 +1,9 @@
package org.jetbrains.compose.resources
import com.squareup.kotlinpoet.*
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import java.nio.file.Path
import java.util.SortedMap
import java.util.TreeMap
import java.util.*
import kotlin.io.path.invariantSeparatorsPathString
internal enum class ResourceType(val typeName: String) {
@ -35,6 +35,9 @@ private fun ResourceType.getClassName(): ClassName = when (this) {
}
private val resourceItemClass = ClassName("org.jetbrains.compose.resources", "ResourceItem")
private val experimentalAnnotation = AnnotationSpec.builder(
ClassName("org.jetbrains.compose.resources", "ExperimentalResourceApi")
).build()
private fun CodeBlock.Builder.addQualifiers(resourceItem: ResourceItem): CodeBlock.Builder {
val languageQualifier = ClassName("org.jetbrains.compose.resources", "LanguageQualifier")
@ -101,33 +104,35 @@ private fun CodeBlock.Builder.addQualifiers(resourceItem: ResourceItem): CodeBlo
return this
}
internal fun getResFileSpec(
// We need to divide accessors by different files because
//
// if all accessors are generated in a single object
// then a build may fail with: org.jetbrains.org.objectweb.asm.MethodTooLargeException: Method too large: Res$drawable.<clinit> ()V
// e.g. https://github.com/JetBrains/compose-multiplatform/issues/4285
//
// if accessor initializers are extracted from the single object but located in the same file
// then a build may fail with: org.jetbrains.org.objectweb.asm.ClassTooLargeException: Class too large: Res$drawable
private const val ITEMS_PER_FILE_LIMIT = 500
internal fun getResFileSpecs(
//type -> id -> items
resources: Map<ResourceType, Map<String, List<ResourceItem>>>,
packageName: String
): FileSpec =
FileSpec.builder(packageName, "Res").apply {
addAnnotation(
): List<FileSpec> {
val files = mutableListOf<FileSpec>()
val resClass = FileSpec.builder(packageName, "Res").also { file ->
file.addAnnotation(
AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class")
.addMember("org.jetbrains.compose.resources.ExperimentalResourceApi::class")
.build()
)
//we need to sort it to generate the same code on different platforms
val sortedResources = sortResources(resources)
addType(TypeSpec.objectBuilder("Res").apply {
addModifiers(KModifier.INTERNAL)
addAnnotation(
AnnotationSpec.builder(
ClassName("org.jetbrains.compose.resources", "ExperimentalResourceApi")
).build()
)
file.addType(TypeSpec.objectBuilder("Res").also { resObject ->
resObject.addModifiers(KModifier.INTERNAL)
resObject.addAnnotation(experimentalAnnotation)
//readFileBytes
val readResourceBytes = MemberName("org.jetbrains.compose.resources", "readResourceBytes")
addFunction(
resObject.addFunction(
FunSpec.builder("readBytes")
.addKdoc(
"""
@ -145,65 +150,82 @@ internal fun getResFileSpec(
.addStatement("return %M(path)", readResourceBytes) //todo: add module ID here
.build()
)
val types = sortedResources.map { (type, idToResources) ->
getResourceTypeObject(type, idToResources)
ResourceType.values().forEach { type ->
resObject.addType(TypeSpec.objectBuilder(type.typeName).build())
}
addTypes(types)
}.build())
sortedResources
.flatMap { (type, idToResources) ->
idToResources.map { (name, items) ->
getResourceInitializer(name, type, items)
}
}
.forEach { addFunction(it) }
}.build()
files.add(resClass)
private fun getterName(resourceType: ResourceType, resourceName: String): String =
"get_${resourceType.typeName}_$resourceName"
private fun getResourceTypeObject(type: ResourceType, nameToResources: Map<String, List<ResourceItem>>) =
TypeSpec.objectBuilder(type.typeName).apply {
nameToResources.keys
.forEach { name ->
addProperty(
PropertySpec
.builder(name, type.getClassName())
.initializer(getterName(type, name) + "()")
.build()
)
}
}.build()
//we need to sort it to generate the same code on different platforms
sortResources(resources).forEach { (type, idToResources) ->
val chunks = idToResources.keys.chunked(ITEMS_PER_FILE_LIMIT)
private fun getResourceInitializer(name: String, type: ResourceType, items: List<ResourceItem>): FunSpec {
val propertyTypeName = type.getClassName()
val resourceId = "${type}:${name}"
return FunSpec.builder(getterName(type, name))
.addModifiers(KModifier.PRIVATE)
.returns(propertyTypeName)
.addStatement(
CodeBlock.builder()
.add("return %T(\n", propertyTypeName).withIndent {
add("\"$resourceId\",")
if (type == ResourceType.STRING) add(" \"$name\",")
withIndent {
add("\nsetOf(\n").withIndent {
items.forEach { item ->
add("%T(", resourceItemClass)
add("setOf(").addQualifiers(item).add("), ")
//file separator should be '/' on all platforms
add("\"${item.path.invariantSeparatorsPathString}\"") //todo: add module ID here
add("),\n")
}
}
add(")\n")
}
}
.add(")")
.build().toString()
chunks.forEachIndexed { index, ids ->
files.add(
getChunkFileSpec(type, index, packageName, idToResources.subMap(ids.first(), true, ids.last(), true))
)
}
}
return files
}
private fun getChunkFileSpec(
type: ResourceType,
index: Int,
packageName: String,
idToResources: Map<String, List<ResourceItem>>
): FileSpec {
val chunkClassName = type.typeName.uppercaseFirstChar() + index
return FileSpec.builder(packageName, chunkClassName).also { chunkFile ->
chunkFile.addAnnotation(
AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class")
.build()
)
.build()
val objectSpec = TypeSpec.objectBuilder(chunkClassName).also { typeObject ->
typeObject.addModifiers(KModifier.PRIVATE)
typeObject.addAnnotation(experimentalAnnotation)
val properties = idToResources.map { (resName, items) ->
PropertySpec.builder(resName, type.getClassName())
.initializer(
CodeBlock.builder()
.add("%T(\n", type.getClassName()).withIndent {
add("\"${type}:${resName}\",")
if (type == ResourceType.STRING) add(" \"$resName\",")
withIndent {
add("\nsetOf(\n").withIndent {
items.forEach { item ->
add("%T(", resourceItemClass)
add("setOf(").addQualifiers(item).add("), ")
//file separator should be '/' on all platforms
add("\"${item.path.invariantSeparatorsPathString}\"") //todo: add module ID here
add("),\n")
}
}
add(")\n")
}
}
.add(")")
.build().toString()
)
.build()
}
typeObject.addProperties(properties)
}.build()
chunkFile.addType(objectSpec)
idToResources.keys.forEach { resName ->
val accessor = PropertySpec.builder(resName, type.getClassName(), KModifier.INTERNAL)
.receiver(ClassName(packageName, "Res", type.typeName))
.addAnnotation(experimentalAnnotation)
.getter(FunSpec.getterBuilder().addStatement("return $chunkClassName.$resName").build())
.build()
chunkFile.addProperty(accessor)
}
}.build()
}
private fun sortResources(

140
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt

@ -4,6 +4,7 @@ import org.jetbrains.compose.test.utils.*
import org.junit.jupiter.api.Test
import java.io.File
import java.util.zip.ZipFile
import kotlin.io.path.relativeTo
import kotlin.test.*
class ResourcesTest : GradlePluginTestBase() {
@ -11,9 +12,9 @@ class ResourcesTest : GradlePluginTestBase() {
fun testGeneratedAccessors(): Unit = with(testProject("misc/commonResources")) {
//check generated resource's accessors
gradle("generateComposeResClass").checks {
assertEqualTextFiles(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt"),
file("expected/Res.kt")
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources"),
file("expected")
)
}
@ -23,8 +24,8 @@ class ResourcesTest : GradlePluginTestBase() {
)
gradle("generateComposeResClass").checks {
assertNotEqualTextFiles(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt"),
file("expected/Res.kt")
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Drawable0.kt"),
file("expected/Drawable0.kt")
)
}
@ -123,6 +124,12 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable/vector_3.xml").renameTo(
file("src/commonMain/composeResources/drawable/vector_2.xml")
)
gradle("generateComposeResClass").checks {
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources"),
file("expected")
)
}
}
@Test
@ -271,9 +278,9 @@ class ResourcesTest : GradlePluginTestBase() {
@Test
fun testEmptyResClass(): Unit = with(testProject("misc/emptyResources")) {
gradle("generateComposeResClass").checks {
assertEqualTextFiles(
file("build/generated/compose/resourceGenerator/kotlin/app/group/empty_res/generated/resources/Res.kt"),
file("expected/Res.kt")
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/empty_res/generated/resources"),
file("expected")
)
}
}
@ -281,96 +288,30 @@ class ResourcesTest : GradlePluginTestBase() {
@Test
fun testJvmOnlyProject(): Unit = with(testProject("misc/jvmOnlyResources")) {
gradle("generateComposeResClass").checks {
assertEqualTextFiles(
file("build/generated/compose/resourceGenerator/kotlin/me/app/jvmonlyresources/generated/resources/Res.kt"),
file("expected/Res.kt")
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/me/app/jvmonlyresources/generated/resources"),
file("expected")
)
}
gradle("jar")
}
//https://github.com/JetBrains/compose-multiplatform/issues/4194
//https://github.com/JetBrains/compose-multiplatform/issues/4285
//
// 25_000 icons + 25_000 strings!!!
@Test
fun testHugeNumberOfStrings(): Unit = with(
//disable cache for the test because the generateStringFiles task doesn't support it
testProject("misc/commonResources", defaultTestEnvironment.copy(useGradleConfigurationCache = false))
fun testHugeNumberOfResources(): Unit = with(
//disable cache for the test because the generateResourceFiles task doesn't support it
testProject("misc/hugeResources", defaultTestEnvironment.copy(useGradleConfigurationCache = false))
) {
file("build.gradle.kts").let { f ->
val originText = f.readText()
f.writeText(
buildString {
appendLine("import java.util.Locale")
append(originText)
appendLine()
append("""
val template = ""${'"'}
<resources>
<string name="app_name">Compose Resources App</string>
<string name="hello">😊 Hello world!</string>
<string name="multi_line">Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Donec eget turpis ac sem ultricies consequat.</string>
<string name="str_template">Hello, %1${'$'}{"$"}s! You have %2${'$'}{"$"}d new messages.</string>
<string-array name="str_arr">
<item>item 1</item>
<item>item 2</item>
<item>item 3</item>
</string-array>
[ADDITIONAL_STRINGS]
</resources>
""${'"'}.trimIndent()
val generateStringFiles = tasks.register("generateStringFiles") {
val numberOfLanguages = 20
val numberOfStrings = 500
val langs = Locale.getAvailableLocales()
.map { it.language }
.filter { it.count() == 2 }
.sorted()
.distinct()
.take(numberOfLanguages)
.toList()
val resourcesFolder = project.file("src/commonMain/composeResources")
doLast {
// THIS REMOVES THE `values` FOLDER IN `composeResources`
// THIS REMOVES THE `values` FOLDER IN `composeResources`
// Necessary when reducing the number of languages.
resourcesFolder.listFiles()?.filter { it.name.startsWith("values") }?.forEach {
it.deleteRecursively()
}
langs.forEachIndexed { langIndex, lang ->
val additionalStrings =
(0 until numberOfStrings).joinToString(System.lineSeparator()) { index ->
""${'"'}
<string name="string_${'$'}{index.toString().padStart(4, '0')}">String ${'$'}index in lang ${'$'}lang</string>
""${'"'}.trimIndent()
}
val langFile = if (langIndex == 0) {
File(resourcesFolder, "values/strings.xml")
} else {
File(resourcesFolder, "values-${'$'}lang/strings.xml")
}
langFile.parentFile.mkdirs()
langFile.writeText(template.replace("[ADDITIONAL_STRINGS]", additionalStrings))
}
}
}
tasks.named("generateComposeResClass") {
dependsOn(generateStringFiles)
}
""".trimIndent())
}
)
}
gradle("desktopJar").checks {
check.taskSuccessful(":generateStringFiles")
gradle("compileKotlinDesktop").checks {
check.taskSuccessful(":generateResourceFiles")
check.taskSuccessful(":generateComposeResClass")
assertEquals(513, file("src/commonMain/composeResources/values/strings.xml").readLines().size)
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/huge/generated/resources"),
file("expected")
)
}
}
@ -379,4 +320,25 @@ class ResourcesTest : GradlePluginTestBase() {
fun testBundledKotlinPoet(): Unit = with(testProject("misc/bundledKotlinPoet")) {
gradle("generateBuildConfig")
}
private fun assertDirectoriesContentEquals(actual: File, expected: File) {
require(expected.isDirectory)
require(actual.isDirectory)
assertEquals(expected.exists(), actual.exists())
val expectedPath = expected.toPath()
val actualPath = actual.toPath()
expected.walkTopDown().forEach { expectedFile ->
if (!expectedFile.isDirectory) {
val actualFile = actualPath.resolve(expectedFile.toPath().relativeTo(expectedPath)).toFile()
assertEqualTextFiles(actualFile, expectedFile)
}
}
val expectedFilesCount = expected.walkTopDown()
.map { it.toPath().relativeTo(expectedPath) }.sorted().joinToString("\n")
val actualFilesCount = actual.walkTopDown()
.map { it.toPath().relativeTo(actualPath) }.sorted().joinToString("\n")
assertEquals(expectedFilesCount, actualFilesCount)
}
}

2
gradle-plugins/compose/src/test/test-projects/application/macOptions/Expected-Info.plist

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>LSMinimumSystemVersion</key>
<string>10.13</string>
<string>12.0</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleAllowMixedLocalizations</key>

1
gradle-plugins/compose/src/test/test-projects/application/macOptions/build.gradle

@ -30,6 +30,7 @@ compose.desktop {
packageName = "TestPackage"
macOS {
dockName = "CustomDockName"
minimumSystemVersion = "12.0"
infoPlist {
extraKeysRawXml = extraInfoPlistKeys
}

64
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Drawable0.kt

@ -0,0 +1,64 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package app.group.resources_test.generated.resources
import kotlin.OptIn
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi
private object Drawable0 {
public val _3_strange_name: DrawableResource = org.jetbrains.compose.resources.DrawableResource(
"drawable:_3_strange_name",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/3-strange-name.xml"),
)
)
public val camelCaseName: DrawableResource = org.jetbrains.compose.resources.DrawableResource(
"drawable:camelCaseName",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/camelCaseName.xml"),
)
)
public val vector: DrawableResource = org.jetbrains.compose.resources.DrawableResource(
"drawable:vector",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("au"),
org.jetbrains.compose.resources.RegionQualifier("US"), ), "drawable-au-rUS/vector.xml"),
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.ThemeQualifier.DARK,
org.jetbrains.compose.resources.LanguageQualifier("ge"), ),
"drawable-dark-ge/vector.xml"),
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("en"),
), "drawable-en/vector.xml"),
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/vector.xml"),
)
)
public val vector_2: DrawableResource = org.jetbrains.compose.resources.DrawableResource(
"drawable:vector_2",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/vector_2.xml"),
)
)
}
@ExperimentalResourceApi
internal val Res.drawable._3_strange_name: DrawableResource
get() = Drawable0._3_strange_name
@ExperimentalResourceApi
internal val Res.drawable.camelCaseName: DrawableResource
get() = Drawable0.camelCaseName
@ExperimentalResourceApi
internal val Res.drawable.vector: DrawableResource
get() = Drawable0.vector
@ExperimentalResourceApi
internal val Res.drawable.vector_2: DrawableResource
get() = Drawable0.vector_2

21
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Font0.kt

@ -0,0 +1,21 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package app.group.resources_test.generated.resources
import kotlin.OptIn
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.FontResource
@ExperimentalResourceApi
private object Font0 {
public val emptyFont: FontResource = org.jetbrains.compose.resources.FontResource(
"font:emptyFont",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "font/emptyFont.otf"),
)
)
}
@ExperimentalResourceApi
internal val Res.font.emptyFont: FontResource
get() = Font0.emptyFont

165
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Res.kt

@ -1,6 +1,6 @@
@file:OptIn(
org.jetbrains.compose.resources.InternalResourceApi::class,
org.jetbrains.compose.resources.ExperimentalResourceApi::class,
org.jetbrains.compose.resources.InternalResourceApi::class,
org.jetbrains.compose.resources.ExperimentalResourceApi::class,
)
package app.group.resources_test.generated.resources
@ -8,161 +8,24 @@ package app.group.resources_test.generated.resources
import kotlin.ByteArray
import kotlin.OptIn
import kotlin.String
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.FontResource
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.readResourceBytes
@ExperimentalResourceApi
internal object Res {
/**
* Reads the content of the resource file at the specified path and returns it as a byte array.
*
* Example: `val bytes = Res.readBytes("files/key.bin")`
*
* @param path The path of the file to read in the compose resource's directory.
* @return The content of the file as a byte array.
*/
public suspend fun readBytes(path: String): ByteArray = readResourceBytes(path)
/**
* Reads the content of the resource file at the specified path and returns it as a byte array.
*
* Example: `val bytes = Res.readBytes("files/key.bin")`
*
* @param path The path of the file to read in the compose resource's directory.
* @return The content of the file as a byte array.
*/
public suspend fun readBytes(path: String): ByteArray = readResourceBytes(path)
public object drawable {
public val _3_strange_name: DrawableResource = get_drawable__3_strange_name()
public object drawable
public val camelCaseName: DrawableResource = get_drawable_camelCaseName()
public object string
public val vector: DrawableResource = get_drawable_vector()
public val vector_2: DrawableResource = get_drawable_vector_2()
}
public object string {
public val PascalCase: StringResource = get_string_PascalCase()
public val _1_kebab_case: StringResource = get_string__1_kebab_case()
public val app_name: StringResource = get_string_app_name()
public val camelCase: StringResource = get_string_camelCase()
public val hello: StringResource = get_string_hello()
public val multi_line: StringResource = get_string_multi_line()
public val str_arr: StringResource = get_string_str_arr()
public val str_template: StringResource = get_string_str_template()
}
public object font {
public val emptyFont: FontResource = get_font_emptyFont()
}
public object font
}
private fun get_drawable__3_strange_name(): DrawableResource =
org.jetbrains.compose.resources.DrawableResource(
"drawable:_3_strange_name",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/3-strange-name.xml"),
)
)
private fun get_drawable_camelCaseName(): DrawableResource =
org.jetbrains.compose.resources.DrawableResource(
"drawable:camelCaseName",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/camelCaseName.xml"),
)
)
private fun get_drawable_vector(): DrawableResource =
org.jetbrains.compose.resources.DrawableResource(
"drawable:vector",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("au"),
org.jetbrains.compose.resources.RegionQualifier("US"), ), "drawable-au-rUS/vector.xml"),
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.ThemeQualifier.DARK,
org.jetbrains.compose.resources.LanguageQualifier("ge"), ), "drawable-dark-ge/vector.xml"),
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("en"),
), "drawable-en/vector.xml"),
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/vector.xml"),
)
)
private fun get_drawable_vector_2(): DrawableResource =
org.jetbrains.compose.resources.DrawableResource(
"drawable:vector_2",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/vector_2.xml"),
)
)
private fun get_string_PascalCase(): StringResource =
org.jetbrains.compose.resources.StringResource(
"string:PascalCase", "PascalCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string__1_kebab_case(): StringResource =
org.jetbrains.compose.resources.StringResource(
"string:_1_kebab_case", "_1_kebab_case",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string_app_name(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:app_name", "app_name",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:camelCase", "camelCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string_hello(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:hello", "hello",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string_multi_line(): StringResource =
org.jetbrains.compose.resources.StringResource(
"string:multi_line", "multi_line",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string_str_arr(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_arr", "str_arr",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_string_str_template(): StringResource =
org.jetbrains.compose.resources.StringResource(
"string:str_template", "str_template",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
private fun get_font_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource(
"font:emptyFont",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "font/emptyFont.otf"),
)
)

98
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/String0.kt

@ -0,0 +1,98 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package app.group.resources_test.generated.resources
import kotlin.OptIn
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object String0 {
public val PascalCase: StringResource = org.jetbrains.compose.resources.StringResource(
"string:PascalCase", "PascalCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val _1_kebab_case: StringResource = org.jetbrains.compose.resources.StringResource(
"string:_1_kebab_case", "_1_kebab_case",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val app_name: StringResource = org.jetbrains.compose.resources.StringResource(
"string:app_name", "app_name",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val camelCase: StringResource = org.jetbrains.compose.resources.StringResource(
"string:camelCase", "camelCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val hello: StringResource = org.jetbrains.compose.resources.StringResource(
"string:hello", "hello",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val multi_line: StringResource = org.jetbrains.compose.resources.StringResource(
"string:multi_line", "multi_line",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val str_arr: StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_arr", "str_arr",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
public val str_template: StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_template", "str_template",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
)
)
}
@ExperimentalResourceApi
internal val Res.string.PascalCase: StringResource
get() = String0.PascalCase
@ExperimentalResourceApi
internal val Res.string._1_kebab_case: StringResource
get() = String0._1_kebab_case
@ExperimentalResourceApi
internal val Res.string.app_name: StringResource
get() = String0.app_name
@ExperimentalResourceApi
internal val Res.string.camelCase: StringResource
get() = String0.camelCase
@ExperimentalResourceApi
internal val Res.string.hello: StringResource
get() = String0.hello
@ExperimentalResourceApi
internal val Res.string.multi_line: StringResource
get() = String0.multi_line
@ExperimentalResourceApi
internal val Res.string.str_arr: StringResource
get() = String0.str_arr
@ExperimentalResourceApi
internal val Res.string.str_template: StringResource
get() = String0.str_template

3
gradle-plugins/compose/src/test/test-projects/misc/commonResources/src/commonMain/kotlin/App.kt

@ -6,6 +6,9 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.group.resources_test.generated.resources.Res
import app.group.resources_test.generated.resources.app_name
import app.group.resources_test.generated.resources.emptyFont
import app.group.resources_test.generated.resources.vector
import org.jetbrains.compose.resources.*
@OptIn(ExperimentalResourceApi::class)

6
gradle-plugins/compose/src/test/test-projects/misc/emptyResources/expected/Res.kt

@ -22,4 +22,10 @@ internal object Res {
* @return The content of the file as a byte array.
*/
public suspend fun readBytes(path: String): ByteArray = readResourceBytes(path)
public object drawable
public object string
public object font
}

50
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/build.gradle.kts

@ -0,0 +1,50 @@
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
}
group = "app.group"
kotlin {
jvm("desktop")
sourceSets {
commonMain {
dependencies {
implementation(compose.runtime)
implementation(compose.material)
implementation(compose.components.resources)
}
}
}
}
val generateResourceFiles = tasks.register("generateResourceFiles") {
val resourcesFolder = project.file("src/commonMain/composeResources")
val count = 25_000
doLast {
val txt = buildString {
appendLine("<resources>")
repeat(count) {
appendLine(" <string name=\"str_${it}\">str_${it}</string>")
}
appendLine("</resources>")
}
File(resourcesFolder, "values/strings.xml").apply {
parentFile.mkdirs()
writeText(txt)
}
}
doLast {
repeat(count) {
File(resourcesFolder, "drawable/icon_$it.xml").apply {
parentFile.mkdirs()
createNewFile() //empty file
}
}
}
}
tasks.named("generateComposeResClass") {
dependsOn(generateResourceFiles)
}

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable0.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable1.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable10.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable11.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable12.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable13.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable14.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable15.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable16.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable17.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable18.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable19.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable2.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable20.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable21.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable22.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable23.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable24.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable25.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable26.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable27.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable28.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable29.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable3.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable30.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable31.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable32.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable33.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable34.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable35.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable36.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable37.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable38.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable39.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable4.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable40.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable41.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable42.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable43.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable44.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable45.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable46.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable47.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable48.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable49.kt

File diff suppressed because it is too large Load Diff

5510
gradle-plugins/compose/src/test/test-projects/misc/hugeResources/expected/Drawable5.kt

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save