Browse Source

Merge branch 'master' into update-dmg-and-pkg-version-validation

pull/4874/head
Sam Liddle 4 months ago
parent
commit
3d48275d97
  1. 58
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 5
      .github/ISSUE_TEMPLATE/config.yml
  3. 8
      .github/ISSUE_TEMPLATE/enhancement.md
  4. 48
      .github/ISSUE_TEMPLATE/performance.md
  5. 4
      .github/workflows/gradle-plugin.yml
  6. 145
      CHANGELOG.md
  7. 1
      benchmarks/ios/jvm-vs-kotlin-native/build.gradle.kts
  8. 10
      benchmarks/ios/jvm-vs-kotlin-native/gradle.properties
  9. 1
      benchmarks/ios/jvm-vs-kotlin-native/settings.gradle.kts
  10. 0
      benchmarks/ios/jvm-vs-kotlin-native/src/commonMain/composeResources/drawable/compose-multiplatform.xml
  11. 4
      benchmarks/ios/jvm-vs-kotlin-native/src/commonMain/kotlin/benchmarks/animation/AnimatedVisibility.kt
  12. 1
      benchmarks/kn-performance/build.gradle.kts
  13. 4
      benchmarks/kn-performance/gradle.properties
  14. 1
      benchmarks/kn-performance/settings.gradle.kts
  15. 0
      benchmarks/kn-performance/src/commonMain/composeResources/drawable/compose-multiplatform.xml
  16. 4
      benchmarks/kn-performance/src/commonMain/kotlin/benchmarks/animation/AnimatedVisibility.kt
  17. 114
      components/SplitPane/demo/src/jvmMain/kotlin/org/jetbrains/compose/splitpane/demo/Main.kt
  18. 7
      components/SplitPane/library/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneState.kt
  19. 3
      components/build.gradle.kts
  20. 4
      components/gradle.properties
  21. 2
      components/gradle/libs.versions.toml
  22. 10
      components/resources/demo/shared/build.gradle.kts
  23. 18
      components/resources/demo/shared/src/androidMain/kotlin/org/jetbrains/compose/resources/demo/shared/main.android.kt
  24. 112
      components/resources/library/api/android/library.api
  25. 108
      components/resources/library/api/desktop/library.api
  26. 108
      components/resources/library/api/library.klib.api
  27. 12
      components/resources/library/build.gradle.kts
  28. 13
      components/resources/library/src/androidMain/AndroidManifest.xml
  29. 81
      components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/AndroidContextProvider.kt
  30. 5
      components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/FontResources.android.kt
  31. 13
      components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ImageResources.android.kt
  32. 93
      components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.android.kt
  33. 30
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ImageResources.kt
  34. 4
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/PluralStringResources.kt
  35. 42
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/Qualifier.kt
  36. 51
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceEnvironment.kt
  37. 6
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceReader.kt
  38. 2
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringArrayResources.kt
  39. 4
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt
  40. 40
      components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.kt
  41. 8
      components/resources/library/src/desktopMain/kotlin/org/jetbrains/compose/resources/ResourceReader.desktop.kt
  42. 2
      components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt
  43. 31
      components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/ImageResources.skiko.kt
  44. 7
      components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/ResourceReader.skiko.kt
  45. 37
      components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt
  46. 6
      components/settings.gradle.kts
  47. 2
      components/ui-tooling-preview/library/build.gradle.kts
  48. 2
      examples/chat/iosApp/iosApp/Info.plist
  49. 2
      examples/codeviewer/iosApp/iosApp/Info.plist
  50. 2
      examples/graphics-2d/iosApp/iosApp/Info.plist
  51. 2
      examples/imageviewer/iosApp/iosApp/Info.plist
  52. 7
      examples/imageviewer/shared/src/androidMain/AndroidManifest.xml
  53. 1
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/Dependencies.kt
  54. 18
      examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/DesktopImageStorage.kt
  55. 2
      examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt
  56. 12
      examples/imageviewer/shared/src/desktopTest/kotlin/ImageViewerTest.kt
  57. 33
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt
  58. 19
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/storage/IosImageStorage.ios.kt
  59. 21
      examples/imageviewer/webApp/build.gradle.kts
  60. 3
      examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/ui/components/Button.kt
  61. 2
      examples/jetsnack/ios/iosApp/Info.plist
  62. 2
      examples/todoapp-lite/iosApp/iosApp/Info.plist
  63. 2
      examples/widgets-gallery/iosApp/iosApp/Info.plist
  64. 3
      experimental/examples/intellij-plugin-with-experimental-shared-base/.gitignore
  65. 23
      experimental/examples/intellij-plugin-with-experimental-shared-base/.run/runIde.run.xml
  66. 20
      experimental/examples/intellij-plugin-with-experimental-shared-base/README.md
  67. 38
      experimental/examples/intellij-plugin-with-experimental-shared-base/build.gradle.kts
  68. 5
      experimental/examples/intellij-plugin-with-experimental-shared-base/gradle.properties
  69. BIN
      experimental/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.jar
  70. 5
      experimental/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.properties
  71. 185
      experimental/examples/intellij-plugin-with-experimental-shared-base/gradlew
  72. 89
      experimental/examples/intellij-plugin-with-experimental-shared-base/gradlew.bat
  73. BIN
      experimental/examples/intellij-plugin-with-experimental-shared-base/screenshots/ide-run-configuration.png
  74. 8
      experimental/examples/intellij-plugin-with-experimental-shared-base/settings.gradle.kts
  75. 70
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/ComposeDemoAction.kt
  76. 28
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/IntellijTheme.kt
  77. 108
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/color/ColorLineMarkerProvider.kt
  78. 128
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/color/ColorPicker.kt
  79. 75
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/color/HSV.kt
  80. 44
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/panel/ComposeToolWindow.kt
  81. 36
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/panel/CounterPanel.kt
  82. 10
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/panel/CounterState.kt
  83. 9
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Color.kt
  84. 11
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Shape.kt
  85. 46
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Theme.kt
  86. 43
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Type.kt
  87. 61
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/SwingColor.kt
  88. 13
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/ThemeChangeListener.kt
  89. 57
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Buttons.kt
  90. 71
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/LazyScrollable.kt
  91. 39
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Loaders.kt
  92. 53
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/TextInputs.kt
  93. 90
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Toggles.kt
  94. 29
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/META-INF/plugin.xml
  95. 1
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/icons/compose.svg
  96. 25
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/test/kotlin/com/jetbrains/compose/color/ColorPickerUITest.kt
  97. 22
      experimental/examples/intellij-plugin-with-experimental-shared-base/src/test/kotlin/com/jetbrains/compose/color/HSVTest.kt
  98. 8
      gradle-plugins/compose/build.gradle.kts
  99. 2
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerCompatibility.kt
  100. 55
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt
  101. Some files were not shown because too many files have changed in this diff Show More

58
.github/ISSUE_TEMPLATE/bug_report.md

@ -1,58 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ['submitted', 'bug']
assignees: ''
---
<!--
If the bug is Android-only, report it in the [Jetpack Compose tracker](https://issuetracker.google.com/issues/new?component=612128)
-->
**Describe the bug**
A clear and concise description of what the bug is.
**Affected platforms**
<!-- Select one or multiple affected platforms below: -->
- Desktop (Windows, Linux, macOS)
- iOS
- Web (K/Wasm) - Canvas based API
- Web (K/JS) - Canvas based API
- Web (K/JS) - HTML library <!-- Note that Compose HTML is not a multiplatform library. It can be used ONLY with Kotlin/JS. -->
**Versions**
- Libraries:
- Compose Multiplatform version:
- Navigation Multiplatform version (for related issues):
- ...
- Kotlin version:
- OS version(s) (required for Desktop and iOS issues):
- OS architecture (x86 or arm64):
- Device (model or simulator for iOS issues):
- JDK (for desktop issues):
**To Reproduce**
Steps to reproduce the behavior:
1. Run this code snippet:
```kt
@Composable
fun BugReproduction() {
// ...
}
```
2. Click on '...'
3. Scroll down to '...'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

5
.github/ISSUE_TEMPLATE/config.yml

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Create
url: https://youtrack.jetbrains.com/newIssue?project=CMP
about: Please report new issues to the JetBrains YouTrack

8
.github/ISSUE_TEMPLATE/enhancement.md

@ -1,8 +0,0 @@
---
name: Enhancement
about: 'New feature or request'
title: ''
labels: 'submitted, enhancement'
assignees: ''
---

48
.github/ISSUE_TEMPLATE/performance.md

@ -1,48 +0,0 @@
---
name: Performance problem
about: Create a report to help us improve
title: ''
labels: ['submitted', 'performance']
assignees: ''
---
**Describe the problem**
Explain the performance issue you're experiencing, including the following details:
- What specific issue did you encounter? (e.g. missing frames, high CPU usage, memory leaks)
- Have you noticed any patterns or specific circumstances under which the problem occurs?
**Affected platforms**
Select one of the platforms below:
- All
- Desktop
- Web (K/Wasm) - Canvas based API
- Web (K/JS) - Canvas based API
- Web (K/JS) - HTML library
- iOS
- Other
If the problem is Android-only, report it in the [Jetpack Compose tracker](https://issuetracker.google.com/issues/new?component=612128)
**Versions**
- Kotlin version:
- Compose Multiplatform version:
- OS version(s) (required for Desktop and iOS issues):
- OS architecture (x86 or arm64):
- JDK (for desktop issues):
**Sample code**
If possible, provide a small piece of code that reproduces the problem. If the code snippet is too large to paste here, please link to a Gist, a GitHub repo, or any other public code repository.
**Reproduction steps**
Please provide a detailed step-by-step guide on how to reproduce the issue you are experiencing.
**Video**
If you're reporting slow app work or missing frames, please provide a video of the problem.
**Profiling data**
Please provide any relevant profiling data that might be helpful. This could include information like FPS, memory usage, CPU time, or any other data that could provide insight into the performance issue.
**Additional information**
Provide any other details that you think might be helpful for us to understand the problem. This could include things like the system configuration, external factors, etc.

4
.github/workflows/gradle-plugin.yml

@ -17,8 +17,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-20.04, macos-14, windows-2022] os: [ubuntu-20.04, macos-14, windows-2022]
gradle: [7.4, 8.7] gradle: [7.4, 8.8]
agp: [8.1.0, 8.4.0] agp: [8.1.0, 8.5.0]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

145
CHANGELOG.md

@ -1,3 +1,148 @@
# 1.7.0-alpha01 (July 2024)
_Changes since 1.6.11_
## Highlights
- [Compose Multiplatform resources are stored in the android assets now. This fixes Android Studio Preview and cases such as a rendering resource files in WebViews or Media Players](https://github.com/JetBrains/compose-multiplatform/pull/4965)
- [Shared Element Transitions](https://developer.android.com/develop/ui/compose/animation/shared-elements)
- [Safe Args in Navigation Compose](https://developer.android.com/guide/navigation/design/type-safety)
## Breaking changes
### Android
- [Minimal supported AGP raised to 8.1.0](https://github.com/JetBrains/compose-multiplatform/pull/4840)
## Features
### iOS
- [Initial iOS floating cursor support](https://github.com/JetBrains/compose-multiplatform-core/pull/1312)
- [Added `accessibilityEnabled: Boolean = true` argument to `UIKitView` and `UIKitViewController`](https://github.com/JetBrains/compose-multiplatform-core/pull/1350)
- [`preferredStatusBarStyle`, `preferredStatysBarAnimation` and `prefersStatusBarHidden` are added to `ComposeUIViewControllerDelegate` to allow status bar appearance modification](https://github.com/JetBrains/compose-multiplatform-core/pull/1378)
### Desktop
- [Add constructor with `RenderSettings` to `ComposePanel`. Added a class `RenderSettings` with `val isVsyncEnabled: Boolean?`. When set to `true` gives a hint to renderer implementation of the particular `ComposePanel` to reduce the latency between the input and visual changes in exchange for possible screen tearing](https://github.com/JetBrains/compose-multiplatform-core/pull/1377)
- [Add public `moveEnabled` and `positionPercentage` setters in `SplitPaneState`](https://github.com/JetBrains/compose-multiplatform/pull/3974)
### Resources
- [Speed resources web rendering up by the reading a cached value instantly](https://github.com/JetBrains/compose-multiplatform/pull/4893)
- [If there is no resource with suitable density, use resource with the most suitable density, otherwise use default (similar to the Android logic)](https://github.com/JetBrains/compose-multiplatform/pull/4969)
- [Add a customization for resources directories. Now it is possible to use e.g downloaded resources](https://github.com/JetBrains/compose-multiplatform/pull/5016)
## Fixes
### Multiple Platforms
- [Fix "ComposePanel. Focus moves to child after focusing/unfocusing the main window"](https://github.com/JetBrains/compose-multiplatform-core/pull/1398)
- [Don't show code completion for non-existenst API in `commonMain` that fails on Android with `NoSuchMethodException`](https://github.com/JetBrains/compose-multiplatform-core/pull/1328)
- [Fix order of interop elements in some cases](https://github.com/JetBrains/compose-multiplatform-core/pull/1340)
- [Fixed `Popup` jerking during ripple effect animation](https://github.com/JetBrains/compose-multiplatform-core/pull/1385)
- [Fix applying `ShaderBrush` to part of `AnnotatedString`](https://github.com/JetBrains/compose-multiplatform-core/pull/1389)
- [Fix text `brush` animation and optimized updating some visual text properties (applying time is reduced up to 40%)](https://github.com/JetBrains/compose-multiplatform-core/pull/1395)
- [Fix initial cursor position in the empty `TextField` with explicitly set `TextAlignment`](https://github.com/JetBrains/compose-multiplatform-core/pull/1354)
- [Fix focus for editable `TextField` inside `ExposedDropdownMenuBox`](https://github.com/JetBrains/compose-multiplatform-core/pull/1423)
### iOS
- [Pressing directional keys on a physical keyboard connected to iOS device doesn't cause a crash](https://github.com/JetBrains/compose-multiplatform-core/pull/1383)
- [Dismissing popup or dialogue within a very short timespan after its creation doesn't cause a crash](https://github.com/JetBrains/compose-multiplatform-core/pull/1384)
- [Fix missing invalidations during native view resize](https://github.com/JetBrains/compose-multiplatform-core/pull/1387)
- [Fixed a memory spike when continuously resizing the `ComposeUIViewController` (such as when used in modal sheet presentation context with different detents)](https://github.com/JetBrains/compose-multiplatform-core/pull/1390)
- [visibility of selection handles in single-line textfields with LTR + RTL text in iOS](https://github.com/JetBrains/compose-multiplatform-core/pull/1331)
### Desktop
- [Fix possible `UninitializedPropertyAccessException` in `desktopTest`](https://github.com/JetBrains/compose-multiplatform-core/pull/1343)
- [Fixed `ComposePanel.requestFocus()`, making it correctly assign focus to the first focusable child](https://github.com/JetBrains/compose-multiplatform-core/pull/1352)
- [When using `ComposePanel` inside a Swing application on macOS, VoiceOver will now correctly go into the `ComposePanel` when traversing accessible elements](https://github.com/JetBrains/compose-multiplatform-core/pull/1362)
- [When using `ComposePanel` inside a Swing application on Windows with NVDA turned on, focus will now correctly go into the `ComposePanel` when traversing with (ctrl)-shift-tab](https://github.com/JetBrains/compose-multiplatform-core/pull/1363)
- [Correctly save `WindowState` with unspecified `size` instead of crashing](https://github.com/JetBrains/compose-multiplatform-core/pull/1394)
- [Fix `IndexOutOfBoundsException` crash on Windows when traversing a11y elements](https://github.com/JetBrains/compose-multiplatform-core/pull/1415)
### Web
- [Process `keydown` and `keyup` keys for identified keys from virtual keyboard](https://github.com/JetBrains/compose-multiplatform-core/pull/1380)
- [Allow preloading the fallback fonts. This enables the usage of emojis and other unicode characters without manually composing the Text with AnnotatedString](https://github.com/JetBrains/compose-multiplatform-core/pull/1400)
- [Make sure the web app distribution doesn't contain a duplicate `skiko.wasm`](https://github.com/JetBrains/compose-multiplatform/pull/4958)
### Resources
- [Delete `contextClassLoader` usage on JVM targets](https://github.com/JetBrains/compose-multiplatform/pull/4895)
- [Create an empty resource dir with "podspec" task instead "podInstall"](https://github.com/JetBrains/compose-multiplatform/pull/4900)
- [Fix resource accessors escaping. Now it is possible to use resources with names: "package", "is", "item_$xxx" etc](https://github.com/JetBrains/compose-multiplatform/pull/4901)
- [Read exactly requested count of bytes from InputStream on jvm platforms](https://github.com/JetBrains/compose-multiplatform/pull/4943)
### Gradle Plugin
- [Make sure tryGetSkikoRuntimeIfNeeded is executed only during the task execution](https://github.com/JetBrains/compose-multiplatform/pull/4918)
- [Delete outdated build services](https://github.com/JetBrains/compose-multiplatform/pull/4959)
## Dependencies
- Gradle Plugin `org.jetbrains.compose`, version `1.7.0-alpha01`. Based on Jetpack Compose libraries:
- [Compiler 1.5.14](https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.14)
- [Runtime 1.7.0-beta03](https://developer.android.com/jetpack/androidx/releases/compose-runtime#1.7.0-beta03)
- [UI 1.7.0-beta03](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.7.0-beta03)
- [Foundation 1.7.0-beta03](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.7.0-beta03)
- [Material 1.7.0-beta03](https://developer.android.com/jetpack/androidx/releases/compose-material#1.7.0-beta03)
- [Material3 1.3.0-beta03](https://developer.android.com/jetpack/androidx/releases/compose-material3#1.3.0-beta03)
- Lifecycle libraries `org.jetbrains.androidx.lifecycle:lifecycle-*:2.8.0`. Based on [Jetpack Lifecycle 2.8.0](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.8.0)
- Navigation libraries `org.jetbrains.androidx.navigation:navigation-*:2.8.0-alpha08`. Based on [Jetpack Navigation 2.8.0-beta03](https://developer.android.com/jetpack/androidx/releases/navigation#2.8.0-beta03)
___
# 1.6.11 (June 2024)
_Changes since 1.6.10_
## Fixes
### Multiple Platforms
- [Fix endless re-layout when layout is invalidated by measure, which includes measuring `TextField(singleLine=true)` with `IntrinsicSize`](https://github.com/JetBrains/compose-multiplatform-core/pull/1355)
- [Fix container size for `Dialog` centering inside `ImageComposeScene`](https://github.com/JetBrains/compose-multiplatform-core/pull/1375)
### iOS
- [Fix crash on iOS 12 caused by usage unavailable `UIMenuController` API](https://github.com/JetBrains/compose-multiplatform-core/pull/1361)
### Desktop
- [Fix `DropdownMenu`/`Popup` positioning when a window is moved to a screen with a different density](https://github.com/JetBrains/compose-multiplatform-core/pull/1333)
- [Fix possible scrolling without animation on some mouse models](https://github.com/JetBrains/compose-multiplatform-core/pull/1326)
### Web
- [Fixed crash when `DatePicker` text field receives illegal input](https://github.com/JetBrains/compose-multiplatform-core/pull/1368)
### Resources
- [Fix a cached font if the resource acessor was changed](https://github.com/JetBrains/compose-multiplatform/pull/4864)
### Gradle Plugin
- [Fix Compose Compiler configuration for Kotlin < 2.0 when kotlin-android or kotlin-js gradle plugins are applied](https://github.com/JetBrains/compose-multiplatform/pull/4879)
## Dependencies
- Gradle Plugin `org.jetbrains.compose`, version `1.6.11`. Based on Jetpack Compose libraries:
- [Compiler 1.5.14](https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.14)
- [Runtime 1.6.7](https://developer.android.com/jetpack/androidx/releases/compose-runtime#1.6.7)
- [UI 1.6.7](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.7)
- [Foundation 1.6.7](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.6.7)
- [Material 1.6.7](https://developer.android.com/jetpack/androidx/releases/compose-material#1.6.7)
- [Material3 1.2.1](https://developer.android.com/jetpack/androidx/releases/compose-material3#1.2.1)
- Lifecycle libraries `org.jetbrains.androidx.lifecycle:lifecycle-*:2.8.0`. Based on [Jetpack Lifecycle 2.8.0](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.8.0)
- Navigation libraries `org.jetbrains.androidx.navigation:navigation-*:2.7.0-alpha07`. Based on [Jetpack Navigation 2.7.7](https://developer.android.com/jetpack/androidx/releases/navigation#2.7.7)
___
# 1.6.10 (May 2024) # 1.6.10 (May 2024)
_Changes since 1.6.2_ _Changes since 1.6.2_

1
benchmarks/ios/jvm-vs-kotlin-native/build.gradle.kts

@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("org.jetbrains.kotlin.plugin.compose")
id("org.jetbrains.compose") id("org.jetbrains.compose")
} }

10
benchmarks/ios/jvm-vs-kotlin-native/gradle.properties

@ -1,13 +1,7 @@
compose.version=1.4.1 compose.version=1.6.11
kotlin.version=1.8.20 kotlin.version=2.0.0
agp.version=7.0.4 agp.version=7.0.4
org.gradle.jvmargs=-Xmx3g org.gradle.jvmargs=-Xmx3g
kotlin.code.style=official
kotlin.native.useEmbeddableCompilerJar=true
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableGranularSourceSetsMetadata=true
# Enable kotlin/native experimental memory model
kotlin.native.binary.memoryModel=experimental
compose.desktop.verbose=true compose.desktop.verbose=true
android.useAndroidX=true android.useAndroidX=true
kotlin.js.webpack.major.version=4 kotlin.js.webpack.major.version=4

1
benchmarks/ios/jvm-vs-kotlin-native/settings.gradle.kts

@ -10,6 +10,7 @@ pluginManagement {
plugins { plugins {
val kotlinVersion = extra["kotlin.version"] as String val kotlinVersion = extra["kotlin.version"] as String
kotlin("multiplatform").version(kotlinVersion) kotlin("multiplatform").version(kotlinVersion)
id("org.jetbrains.kotlin.plugin.compose").version(kotlinVersion)
val composeVersion = extra["compose.version"] as String val composeVersion = extra["compose.version"] as String
id("org.jetbrains.compose").version(composeVersion) id("org.jetbrains.compose").version(composeVersion)
} }

0
benchmarks/ios/jvm-vs-kotlin-native/src/commonMain/resources/compose-multiplatform.xml → benchmarks/ios/jvm-vs-kotlin-native/src/commonMain/composeResources/drawable/compose-multiplatform.xml

4
benchmarks/ios/jvm-vs-kotlin-native/src/commonMain/kotlin/benchmarks/animation/AnimatedVisibility.kt

@ -13,6 +13,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import compose_benchmarks.generated.resources.Res
import compose_benchmarks.generated.resources.compose_multiplatform
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
@ -29,7 +31,7 @@ fun AnimatedVisibility() {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
AnimatedVisibility(showImage) { AnimatedVisibility(showImage) {
Image( Image(
painterResource("compose-multiplatform.xml"), painterResource(Res.drawable.compose_multiplatform),
null null
) )
} }

1
benchmarks/kn-performance/build.gradle.kts

@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("org.jetbrains.kotlin.plugin.compose")
id("org.jetbrains.compose") id("org.jetbrains.compose")
} }

4
benchmarks/kn-performance/gradle.properties

@ -1,5 +1,5 @@
compose.version=1.5.10 compose.version=1.6.11
kotlin.version=1.9.20 kotlin.version=2.0.0
org.gradle.jvmargs=-Xmx3g org.gradle.jvmargs=-Xmx3g
kotlin.native.useEmbeddableCompilerJar=true kotlin.native.useEmbeddableCompilerJar=true
compose.desktop.verbose=true compose.desktop.verbose=true

1
benchmarks/kn-performance/settings.gradle.kts

@ -10,6 +10,7 @@ pluginManagement {
plugins { plugins {
val kotlinVersion = extra["kotlin.version"] as String val kotlinVersion = extra["kotlin.version"] as String
kotlin("multiplatform").version(kotlinVersion) kotlin("multiplatform").version(kotlinVersion)
id("org.jetbrains.kotlin.plugin.compose").version(kotlinVersion)
val composeVersion = extra["compose.version"] as String val composeVersion = extra["compose.version"] as String
id("org.jetbrains.compose").version(composeVersion) id("org.jetbrains.compose").version(composeVersion)
} }

0
benchmarks/kn-performance/src/commonMain/resources/compose-multiplatform.xml → benchmarks/kn-performance/src/commonMain/composeResources/drawable/compose-multiplatform.xml

4
benchmarks/kn-performance/src/commonMain/kotlin/benchmarks/animation/AnimatedVisibility.kt

@ -13,6 +13,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import compose_benchmarks.generated.resources.Res
import compose_benchmarks.generated.resources.compose_multiplatform
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
@ -29,7 +31,7 @@ fun AnimatedVisibility() {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
AnimatedVisibility(showImage) { AnimatedVisibility(showImage) {
Image( Image(
painterResource("compose-multiplatform.xml"), painterResource(Res.drawable.compose_multiplatform),
null null
) )
} }

114
components/SplitPane/demo/src/jvmMain/kotlin/org/jetbrains/compose/splitpane/demo/Main.kt

@ -1,17 +1,34 @@
package org.jetbrains.compose.splitpane.demo package org.jetbrains.compose.splitpane.demo
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.* import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.singleWindowApplication import androidx.compose.ui.window.singleWindowApplication
import org.jetbrains.compose.splitpane.* import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.VerticalSplitPane
import org.jetbrains.compose.splitpane.rememberSplitPaneState
import java.awt.Cursor import java.awt.Cursor
private fun Modifier.cursorForHorizontalResize(): Modifier = private fun Modifier.cursorForHorizontalResize(): Modifier =
@ -24,40 +41,69 @@ fun main() = singleWindowApplication(
MaterialTheme { MaterialTheme {
val splitterState = rememberSplitPaneState() val splitterState = rememberSplitPaneState()
val hSplitterState = rememberSplitPaneState() val hSplitterState = rememberSplitPaneState()
HorizontalSplitPane( var delta by remember { mutableStateOf("20") }
splitPaneState = splitterState var percentage by remember { mutableStateOf("0.20") }
) { Row {
first(20.dp) { Column(verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(10.dp).width(180.dp)) {
Box(Modifier.background(Color.Red).fillMaxSize()) Text("Action panel", fontWeight = FontWeight.Bold)
} Button(onClick = { splitterState.moveEnabled = !splitterState.moveEnabled }) {
second(50.dp) { Text(if (splitterState.moveEnabled) "Freeze V" else "Unfreeze V")
VerticalSplitPane(splitPaneState = hSplitterState) { }
first(50.dp) { Button(onClick = { hSplitterState.moveEnabled = !hSplitterState.moveEnabled }) {
Box(Modifier.background(Color.Blue).fillMaxSize()) Text(if (hSplitterState.moveEnabled) "Freeze H" else "Unfreeze H")
} }
second(20.dp) {
Box(Modifier.background(Color.Green).fillMaxSize()) OutlinedTextField(value = delta, onValueChange = { delta = it }, label = { Text("Delta") })
} Button(onClick = { delta.toFloatOrNull()?.let { splitterState.dispatchRawMovement(it) } }) {
Text("Add delta V")
}
Button(onClick = { delta.toFloatOrNull()?.let { hSplitterState.dispatchRawMovement(it) } }) {
Text("Add delta H")
}
OutlinedTextField(value = percentage, onValueChange = { percentage = it }, label = { Text("Fraction") })
Button(onClick = { percentage.toFloatOrNull()?.let { splitterState.positionPercentage = it } }) {
Text("Set fraction V")
}
Button(onClick = { percentage.toFloatOrNull()?.let { hSplitterState.positionPercentage = it } }) {
Text("Set fraction H")
} }
} }
splitter { HorizontalSplitPane(
visiblePart { splitPaneState = splitterState
Box( ) {
Modifier first(20.dp) {
.width(1.dp) Box(Modifier.background(Color.Red).fillMaxSize())
.fillMaxHeight()
.background(MaterialTheme.colors.background)
)
} }
handle { second(50.dp) {
Box( VerticalSplitPane(splitPaneState = hSplitterState) {
Modifier first(50.dp) {
.markAsHandle() Box(Modifier.background(Color.Blue).fillMaxSize())
.cursorForHorizontalResize() }
.background(SolidColor(Color.Gray), alpha = 0.50f) second(20.dp) {
.width(9.dp) Box(Modifier.background(Color.Green).fillMaxSize())
.fillMaxHeight() }
) }
}
splitter {
visiblePart {
Box(
Modifier
.width(1.dp)
.fillMaxHeight()
.background(MaterialTheme.colors.background)
)
}
handle {
Box(
Modifier
.markAsHandle()
.cursorForHorizontalResize()
.background(SolidColor(Color.Gray), alpha = 0.50f)
.width(9.dp)
.fillMaxHeight()
)
}
} }
} }
} }

7
components/SplitPane/library/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneState.kt

@ -11,10 +11,11 @@ class SplitPaneState(
) { ) {
var moveEnabled by mutableStateOf(moveEnabled) var moveEnabled by mutableStateOf(moveEnabled)
internal set
var positionPercentage by mutableStateOf(initialPositionPercentage) private var _positionPercentage by mutableStateOf(initialPositionPercentage)
internal set var positionPercentage: Float
get() = _positionPercentage
set(value) { _positionPercentage = value.coerceIn(0f, 1f) }
internal var minPosition: Float = 0f internal var minPosition: Float = 0f

3
components/build.gradle.kts

@ -1,10 +1,11 @@
plugins { plugins {
kotlin("multiplatform") apply false kotlin("multiplatform") apply false
id("com.android.library") apply false id("com.android.library") apply false
id("org.jetbrains.kotlinx.binary-compatibility-validator") apply false
} }
subprojects { subprojects {
version = findProperty("deploy.version") ?: property("compose.version")!! version = findProperty("deploy.version")!!
plugins.withId("java") { plugins.withId("java") {
configureIfExists<JavaPluginExtension> { configureIfExists<JavaPluginExtension> {

4
components/gradle.properties

@ -8,12 +8,12 @@ android.useAndroidX=true
#Versions #Versions
kotlin.version=1.9.23 kotlin.version=1.9.23
compose.version=1.6.10-beta02
agp.version=8.2.2 agp.version=8.2.2
compose.version=1.6.10
deploy.version=0.1.0-SNAPSHOT
#Compose #Compose
org.jetbrains.compose.experimental.jscanvas.enabled=true org.jetbrains.compose.experimental.jscanvas.enabled=true
org.jetbrains.compose.experimental.wasm.enabled=true
org.jetbrains.compose.experimental.macos.enabled=true org.jetbrains.compose.experimental.macos.enabled=true
compose.desktop.verbose=true compose.desktop.verbose=true
compose.useMavenLocal=false compose.useMavenLocal=false

2
components/gradle/libs.versions.toml

@ -14,3 +14,5 @@ androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-te
androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "androidx-compose" } androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "androidx-compose" }
androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-compose" } androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-compose" }
androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-compose" } androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-compose" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidx-compose" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidx-compose" }

10
components/resources/demo/shared/build.gradle.kts

@ -52,6 +52,10 @@ kotlin {
desktopMain.dependencies { desktopMain.dependencies {
implementation(compose.desktop.common) implementation(compose.desktop.common)
} }
androidMain.dependencies {
implementation(libs.androidx.ui.tooling)
implementation(libs.androidx.ui.tooling.preview)
}
val nonAndroidMain by creating { val nonAndroidMain by creating {
dependsOn(commonMain.get()) dependsOn(commonMain.get())
@ -73,6 +77,12 @@ android {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.11"
}
} }
compose.experimental { compose.experimental {

18
components/resources/demo/shared/src/androidMain/kotlin/org/jetbrains/compose/resources/demo/shared/main.android.kt

@ -5,9 +5,27 @@
package org.jetbrains.compose.resources.demo.shared package org.jetbrains.compose.resources.demo.shared
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.PreviewContextConfigurationEffect
@Composable @Composable
fun MainView() { fun MainView() {
UseResources() UseResources()
} }
@Preview(showBackground = true)
@Composable
fun ImagesResPreview() {
ImagesRes(PaddingValues())
}
@OptIn(ExperimentalResourceApi::class)
@Preview(showBackground = true)
@Composable
fun FileResPreview() {
PreviewContextConfigurationEffect()
FileRes(PaddingValues())
}

112
components/resources/library/api/android/library.api

@ -0,0 +1,112 @@
public final class org/jetbrains/compose/resources/AndroidContextProviderKt {
public static final fun PreviewContextConfigurationEffect (Landroidx/compose/runtime/Composer;I)V
}
public final class org/jetbrains/compose/resources/DensityQualifier$Companion {
public final fun selectByDensity (F)Lorg/jetbrains/compose/resources/DensityQualifier;
public final fun selectByValue (I)Lorg/jetbrains/compose/resources/DensityQualifier;
}
public final class org/jetbrains/compose/resources/DrawableResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
}
public abstract interface annotation class org/jetbrains/compose/resources/ExperimentalResourceApi : java/lang/annotation/Annotation {
}
public final class org/jetbrains/compose/resources/FontResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
}
public final class org/jetbrains/compose/resources/FontResourcesKt {
public static final fun getFontResourceBytes (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/FontResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class org/jetbrains/compose/resources/FontResources_androidKt {
public static final fun Font-DnXFreY (Lorg/jetbrains/compose/resources/FontResource;Landroidx/compose/ui/text/font/FontWeight;ILandroidx/compose/runtime/Composer;II)Landroidx/compose/ui/text/font/Font;
}
public final class org/jetbrains/compose/resources/ImageResourcesKt {
public static final fun getDrawableResourceBytes (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/DrawableResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun imageResource (Lorg/jetbrains/compose/resources/DrawableResource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/ImageBitmap;
public static final fun painterResource (Lorg/jetbrains/compose/resources/DrawableResource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/painter/Painter;
public static final fun vectorResource (Lorg/jetbrains/compose/resources/DrawableResource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/vector/ImageVector;
}
public abstract interface annotation class org/jetbrains/compose/resources/InternalResourceApi : java/lang/annotation/Annotation {
}
public final class org/jetbrains/compose/resources/MissingResourceException : java/lang/Exception {
public static final field $stable I
public fun <init> (Ljava/lang/String;)V
}
public final class org/jetbrains/compose/resources/PluralStringResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
public final fun getKey ()Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/PluralStringResourcesKt {
public static final fun getPluralString (Lorg/jetbrains/compose/resources/PluralStringResource;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getPluralString (Lorg/jetbrains/compose/resources/PluralStringResource;I[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getPluralString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/PluralStringResource;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getPluralString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/PluralStringResource;I[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun pluralStringResource (Lorg/jetbrains/compose/resources/PluralStringResource;ILandroidx/compose/runtime/Composer;I)Ljava/lang/String;
public static final fun pluralStringResource (Lorg/jetbrains/compose/resources/PluralStringResource;I[Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/String;
}
public abstract interface class org/jetbrains/compose/resources/Qualifier {
}
public abstract class org/jetbrains/compose/resources/Resource {
public static final field $stable I
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Set;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
}
public final class org/jetbrains/compose/resources/ResourceEnvironment {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
}
public final class org/jetbrains/compose/resources/ResourceEnvironmentKt {
public static final fun getSystemResourceEnvironment ()Lorg/jetbrains/compose/resources/ResourceEnvironment;
public static final fun rememberResourceEnvironment (Landroidx/compose/runtime/Composer;I)Lorg/jetbrains/compose/resources/ResourceEnvironment;
}
public final class org/jetbrains/compose/resources/StringArrayResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
public final fun getKey ()Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/StringArrayResourcesKt {
public static final fun getStringArray (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/StringArrayResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getStringArray (Lorg/jetbrains/compose/resources/StringArrayResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun stringArrayResource (Lorg/jetbrains/compose/resources/StringArrayResource;Landroidx/compose/runtime/Composer;I)Ljava/util/List;
}
public final class org/jetbrains/compose/resources/StringResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
public final fun getKey ()Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/StringResourcesKt {
public static final fun getString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/StringResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/StringResource;[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getString (Lorg/jetbrains/compose/resources/StringResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getString (Lorg/jetbrains/compose/resources/StringResource;[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun stringResource (Lorg/jetbrains/compose/resources/StringResource;Landroidx/compose/runtime/Composer;I)Ljava/lang/String;
public static final fun stringResource (Lorg/jetbrains/compose/resources/StringResource;[Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/ThemeQualifier$Companion {
public final fun selectByValue (Z)Lorg/jetbrains/compose/resources/ThemeQualifier;
}
public final class org/jetbrains/compose/resources/vector/xmldom/MalformedXMLException : java/lang/Exception {
public static final field $stable I
public fun <init> (Ljava/lang/String;)V
}

108
components/resources/library/api/desktop/library.api

@ -0,0 +1,108 @@
public final class org/jetbrains/compose/resources/DensityQualifier$Companion {
public final fun selectByDensity (F)Lorg/jetbrains/compose/resources/DensityQualifier;
public final fun selectByValue (I)Lorg/jetbrains/compose/resources/DensityQualifier;
}
public final class org/jetbrains/compose/resources/DrawableResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
}
public abstract interface annotation class org/jetbrains/compose/resources/ExperimentalResourceApi : java/lang/annotation/Annotation {
}
public final class org/jetbrains/compose/resources/FontResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
}
public final class org/jetbrains/compose/resources/FontResourcesKt {
public static final fun getFontResourceBytes (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/FontResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class org/jetbrains/compose/resources/FontResources_skikoKt {
public static final fun Font-DnXFreY (Lorg/jetbrains/compose/resources/FontResource;Landroidx/compose/ui/text/font/FontWeight;ILandroidx/compose/runtime/Composer;II)Landroidx/compose/ui/text/font/Font;
}
public final class org/jetbrains/compose/resources/ImageResourcesKt {
public static final fun getDrawableResourceBytes (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/DrawableResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun imageResource (Lorg/jetbrains/compose/resources/DrawableResource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/ImageBitmap;
public static final fun painterResource (Lorg/jetbrains/compose/resources/DrawableResource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/painter/Painter;
public static final fun vectorResource (Lorg/jetbrains/compose/resources/DrawableResource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/vector/ImageVector;
}
public abstract interface annotation class org/jetbrains/compose/resources/InternalResourceApi : java/lang/annotation/Annotation {
}
public final class org/jetbrains/compose/resources/MissingResourceException : java/lang/Exception {
public static final field $stable I
public fun <init> (Ljava/lang/String;)V
}
public final class org/jetbrains/compose/resources/PluralStringResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
public final fun getKey ()Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/PluralStringResourcesKt {
public static final fun getPluralString (Lorg/jetbrains/compose/resources/PluralStringResource;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getPluralString (Lorg/jetbrains/compose/resources/PluralStringResource;I[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getPluralString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/PluralStringResource;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getPluralString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/PluralStringResource;I[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun pluralStringResource (Lorg/jetbrains/compose/resources/PluralStringResource;ILandroidx/compose/runtime/Composer;I)Ljava/lang/String;
public static final fun pluralStringResource (Lorg/jetbrains/compose/resources/PluralStringResource;I[Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/String;
}
public abstract interface class org/jetbrains/compose/resources/Qualifier {
}
public abstract class org/jetbrains/compose/resources/Resource {
public static final field $stable I
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Set;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
}
public final class org/jetbrains/compose/resources/ResourceEnvironment {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
}
public final class org/jetbrains/compose/resources/ResourceEnvironmentKt {
public static final fun getSystemResourceEnvironment ()Lorg/jetbrains/compose/resources/ResourceEnvironment;
public static final fun rememberResourceEnvironment (Landroidx/compose/runtime/Composer;I)Lorg/jetbrains/compose/resources/ResourceEnvironment;
}
public final class org/jetbrains/compose/resources/StringArrayResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
public final fun getKey ()Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/StringArrayResourcesKt {
public static final fun getStringArray (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/StringArrayResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getStringArray (Lorg/jetbrains/compose/resources/StringArrayResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun stringArrayResource (Lorg/jetbrains/compose/resources/StringArrayResource;Landroidx/compose/runtime/Composer;I)Ljava/util/List;
}
public final class org/jetbrains/compose/resources/StringResource : org/jetbrains/compose/resources/Resource {
public static final field $stable I
public final fun getKey ()Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/StringResourcesKt {
public static final fun getString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/StringResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getString (Lorg/jetbrains/compose/resources/ResourceEnvironment;Lorg/jetbrains/compose/resources/StringResource;[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getString (Lorg/jetbrains/compose/resources/StringResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getString (Lorg/jetbrains/compose/resources/StringResource;[Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun stringResource (Lorg/jetbrains/compose/resources/StringResource;Landroidx/compose/runtime/Composer;I)Ljava/lang/String;
public static final fun stringResource (Lorg/jetbrains/compose/resources/StringResource;[Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/String;
}
public final class org/jetbrains/compose/resources/ThemeQualifier$Companion {
public final fun selectByValue (Z)Lorg/jetbrains/compose/resources/ThemeQualifier;
}
public final class org/jetbrains/compose/resources/vector/xmldom/MalformedXMLException : java/lang/Exception {
public static final field $stable I
public fun <init> (Ljava/lang/String;)V
}

108
components/resources/library/api/library.klib.api

@ -0,0 +1,108 @@
// Klib ABI Dump
// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, macosArm64, macosX64, wasmJs]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true
// Library unique name: <components.resources:library>
abstract interface org.jetbrains.compose.resources/Qualifier // org.jetbrains.compose.resources/Qualifier|null[0]
final class org.jetbrains.compose.resources.vector.xmldom/MalformedXMLException : kotlin/Exception { // org.jetbrains.compose.resources.vector.xmldom/MalformedXMLException|null[0]
constructor <init>(kotlin/String?) // org.jetbrains.compose.resources.vector.xmldom/MalformedXMLException.<init>|<init>(kotlin.String?){}[0]
}
final class org.jetbrains.compose.resources/DrawableResource : org.jetbrains.compose.resources/Resource // org.jetbrains.compose.resources/DrawableResource|null[0]
final class org.jetbrains.compose.resources/FontResource : org.jetbrains.compose.resources/Resource // org.jetbrains.compose.resources/FontResource|null[0]
final class org.jetbrains.compose.resources/MissingResourceException : kotlin/Exception { // org.jetbrains.compose.resources/MissingResourceException|null[0]
constructor <init>(kotlin/String) // org.jetbrains.compose.resources/MissingResourceException.<init>|<init>(kotlin.String){}[0]
}
final class org.jetbrains.compose.resources/PluralStringResource : org.jetbrains.compose.resources/Resource { // org.jetbrains.compose.resources/PluralStringResource|null[0]
final val key // org.jetbrains.compose.resources/PluralStringResource.key|{}key[0]
final fun <get-key>(): kotlin/String // org.jetbrains.compose.resources/PluralStringResource.key.<get-key>|<get-key>(){}[0]
}
final class org.jetbrains.compose.resources/ResourceEnvironment { // org.jetbrains.compose.resources/ResourceEnvironment|null[0]
final fun equals(kotlin/Any?): kotlin/Boolean // org.jetbrains.compose.resources/ResourceEnvironment.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // org.jetbrains.compose.resources/ResourceEnvironment.hashCode|hashCode(){}[0]
}
final class org.jetbrains.compose.resources/StringArrayResource : org.jetbrains.compose.resources/Resource { // org.jetbrains.compose.resources/StringArrayResource|null[0]
final val key // org.jetbrains.compose.resources/StringArrayResource.key|{}key[0]
final fun <get-key>(): kotlin/String // org.jetbrains.compose.resources/StringArrayResource.key.<get-key>|<get-key>(){}[0]
}
final class org.jetbrains.compose.resources/StringResource : org.jetbrains.compose.resources/Resource { // org.jetbrains.compose.resources/StringResource|null[0]
final val key // org.jetbrains.compose.resources/StringResource.key|{}key[0]
final fun <get-key>(): kotlin/String // org.jetbrains.compose.resources/StringResource.key.<get-key>|<get-key>(){}[0]
}
final const val org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule$stableprop // org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule$stableprop|#static{}org_jetbrains_compose_resources_plural_PluralRule$stableprop[0]
final const val org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRuleList$stableprop // org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRuleList$stableprop|#static{}org_jetbrains_compose_resources_plural_PluralRuleList$stableprop[0]
final const val org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRuleParseException$stableprop // org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRuleParseException$stableprop|#static{}org_jetbrains_compose_resources_plural_PluralRuleParseException$stableprop[0]
final const val org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule_Condition_And$stableprop // org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule_Condition_And$stableprop|#static{}org_jetbrains_compose_resources_plural_PluralRule_Condition_And$stableprop[0]
final const val org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule_Condition_Or$stableprop // org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule_Condition_Or$stableprop|#static{}org_jetbrains_compose_resources_plural_PluralRule_Condition_Or$stableprop[0]
final const val org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule_Condition_Relation$stableprop // org.jetbrains.compose.resources.plural/org_jetbrains_compose_resources_plural_PluralRule_Condition_Relation$stableprop|#static{}org_jetbrains_compose_resources_plural_PluralRule_Condition_Relation$stableprop[0]
final const val org.jetbrains.compose.resources.vector.xmldom/org_jetbrains_compose_resources_vector_xmldom_MalformedXMLException$stableprop // org.jetbrains.compose.resources.vector.xmldom/org_jetbrains_compose_resources_vector_xmldom_MalformedXMLException$stableprop|#static{}org_jetbrains_compose_resources_vector_xmldom_MalformedXMLException$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_AsyncCache$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_AsyncCache$stableprop|#static{}org_jetbrains_compose_resources_AsyncCache$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_DrawCache$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_DrawCache$stableprop|#static{}org_jetbrains_compose_resources_DrawCache$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_DrawableResource$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_DrawableResource$stableprop|#static{}org_jetbrains_compose_resources_DrawableResource$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_FontResource$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_FontResource$stableprop|#static{}org_jetbrains_compose_resources_FontResource$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_ImageCache_Bitmap$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_ImageCache_Bitmap$stableprop|#static{}org_jetbrains_compose_resources_ImageCache_Bitmap$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_ImageCache_Svg$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_ImageCache_Svg$stableprop|#static{}org_jetbrains_compose_resources_ImageCache_Svg$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_ImageCache_Vector$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_ImageCache_Vector$stableprop|#static{}org_jetbrains_compose_resources_ImageCache_Vector$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_LanguageQualifier$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_LanguageQualifier$stableprop|#static{}org_jetbrains_compose_resources_LanguageQualifier$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_MissingResourceException$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_MissingResourceException$stableprop|#static{}org_jetbrains_compose_resources_MissingResourceException$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_PluralStringResource$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_PluralStringResource$stableprop|#static{}org_jetbrains_compose_resources_PluralStringResource$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_RegionQualifier$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_RegionQualifier$stableprop|#static{}org_jetbrains_compose_resources_RegionQualifier$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_Resource$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_Resource$stableprop|#static{}org_jetbrains_compose_resources_Resource$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_ResourceEnvironment$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_ResourceEnvironment$stableprop|#static{}org_jetbrains_compose_resources_ResourceEnvironment$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_ResourceItem$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_ResourceItem$stableprop|#static{}org_jetbrains_compose_resources_ResourceItem$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringArrayResource$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringArrayResource$stableprop|#static{}org_jetbrains_compose_resources_StringArrayResource$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringItem_Array$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringItem_Array$stableprop|#static{}org_jetbrains_compose_resources_StringItem_Array$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringItem_Plurals$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringItem_Plurals$stableprop|#static{}org_jetbrains_compose_resources_StringItem_Plurals$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringItem_Value$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringItem_Value$stableprop|#static{}org_jetbrains_compose_resources_StringItem_Value$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringResource$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_StringResource$stableprop|#static{}org_jetbrains_compose_resources_StringResource$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_SvgElement$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_SvgElement$stableprop|#static{}org_jetbrains_compose_resources_SvgElement$stableprop[0]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_SvgPainter$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_SvgPainter$stableprop|#static{}org_jetbrains_compose_resources_SvgPainter$stableprop[0]
final fun org.jetbrains.compose.resources/Font(org.jetbrains.compose.resources/FontResource, androidx.compose.ui.text.font/FontWeight?, androidx.compose.ui.text.font/FontStyle, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.ui.text.font/Font // org.jetbrains.compose.resources/Font|Font(org.jetbrains.compose.resources.FontResource;androidx.compose.ui.text.font.FontWeight?;androidx.compose.ui.text.font.FontStyle;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/getSystemResourceEnvironment(): org.jetbrains.compose.resources/ResourceEnvironment // org.jetbrains.compose.resources/getSystemResourceEnvironment|getSystemResourceEnvironment(){}[0]
final fun org.jetbrains.compose.resources/imageResource(org.jetbrains.compose.resources/DrawableResource, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.graphics/ImageBitmap // org.jetbrains.compose.resources/imageResource|imageResource(org.jetbrains.compose.resources.DrawableResource;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/painterResource(org.jetbrains.compose.resources/DrawableResource, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.graphics.painter/Painter // org.jetbrains.compose.resources/painterResource|painterResource(org.jetbrains.compose.resources.DrawableResource;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/pluralStringResource(org.jetbrains.compose.resources/PluralStringResource, kotlin/Int, androidx.compose.runtime/Composer?, kotlin/Int): kotlin/String // org.jetbrains.compose.resources/pluralStringResource|pluralStringResource(org.jetbrains.compose.resources.PluralStringResource;kotlin.Int;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/pluralStringResource(org.jetbrains.compose.resources/PluralStringResource, kotlin/Int, kotlin/Array<out kotlin/Any>..., androidx.compose.runtime/Composer?, kotlin/Int): kotlin/String // org.jetbrains.compose.resources/pluralStringResource|pluralStringResource(org.jetbrains.compose.resources.PluralStringResource;kotlin.Int;kotlin.Array<out|kotlin.Any>...;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/rememberResourceEnvironment(androidx.compose.runtime/Composer?, kotlin/Int): org.jetbrains.compose.resources/ResourceEnvironment // org.jetbrains.compose.resources/rememberResourceEnvironment|rememberResourceEnvironment(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/stringArrayResource(org.jetbrains.compose.resources/StringArrayResource, androidx.compose.runtime/Composer?, kotlin/Int): kotlin.collections/List<kotlin/String> // org.jetbrains.compose.resources/stringArrayResource|stringArrayResource(org.jetbrains.compose.resources.StringArrayResource;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/stringResource(org.jetbrains.compose.resources/StringResource, androidx.compose.runtime/Composer?, kotlin/Int): kotlin/String // org.jetbrains.compose.resources/stringResource|stringResource(org.jetbrains.compose.resources.StringResource;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/stringResource(org.jetbrains.compose.resources/StringResource, kotlin/Array<out kotlin/Any>..., androidx.compose.runtime/Composer?, kotlin/Int): kotlin/String // org.jetbrains.compose.resources/stringResource|stringResource(org.jetbrains.compose.resources.StringResource;kotlin.Array<out|kotlin.Any>...;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun org.jetbrains.compose.resources/vectorResource(org.jetbrains.compose.resources/DrawableResource, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.graphics.vector/ImageVector // org.jetbrains.compose.resources/vectorResource|vectorResource(org.jetbrains.compose.resources.DrawableResource;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final suspend fun org.jetbrains.compose.resources/getDrawableResourceBytes(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/DrawableResource): kotlin/ByteArray // org.jetbrains.compose.resources/getDrawableResourceBytes|getDrawableResourceBytes(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.DrawableResource){}[0]
final suspend fun org.jetbrains.compose.resources/getFontResourceBytes(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/FontResource): kotlin/ByteArray // org.jetbrains.compose.resources/getFontResourceBytes|getFontResourceBytes(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.FontResource){}[0]
final suspend fun org.jetbrains.compose.resources/getPluralString(org.jetbrains.compose.resources/PluralStringResource, kotlin/Int): kotlin/String // org.jetbrains.compose.resources/getPluralString|getPluralString(org.jetbrains.compose.resources.PluralStringResource;kotlin.Int){}[0]
final suspend fun org.jetbrains.compose.resources/getPluralString(org.jetbrains.compose.resources/PluralStringResource, kotlin/Int, kotlin/Array<out kotlin/Any>...): kotlin/String // org.jetbrains.compose.resources/getPluralString|getPluralString(org.jetbrains.compose.resources.PluralStringResource;kotlin.Int;kotlin.Array<out|kotlin.Any>...){}[0]
final suspend fun org.jetbrains.compose.resources/getPluralString(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/PluralStringResource, kotlin/Int): kotlin/String // org.jetbrains.compose.resources/getPluralString|getPluralString(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.PluralStringResource;kotlin.Int){}[0]
final suspend fun org.jetbrains.compose.resources/getPluralString(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/PluralStringResource, kotlin/Int, kotlin/Array<out kotlin/Any>...): kotlin/String // org.jetbrains.compose.resources/getPluralString|getPluralString(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.PluralStringResource;kotlin.Int;kotlin.Array<out|kotlin.Any>...){}[0]
final suspend fun org.jetbrains.compose.resources/getString(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/StringResource): kotlin/String // org.jetbrains.compose.resources/getString|getString(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.StringResource){}[0]
final suspend fun org.jetbrains.compose.resources/getString(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/StringResource, kotlin/Array<out kotlin/Any>...): kotlin/String // org.jetbrains.compose.resources/getString|getString(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.StringResource;kotlin.Array<out|kotlin.Any>...){}[0]
final suspend fun org.jetbrains.compose.resources/getString(org.jetbrains.compose.resources/StringResource): kotlin/String // org.jetbrains.compose.resources/getString|getString(org.jetbrains.compose.resources.StringResource){}[0]
final suspend fun org.jetbrains.compose.resources/getString(org.jetbrains.compose.resources/StringResource, kotlin/Array<out kotlin/Any>...): kotlin/String // org.jetbrains.compose.resources/getString|getString(org.jetbrains.compose.resources.StringResource;kotlin.Array<out|kotlin.Any>...){}[0]
final suspend fun org.jetbrains.compose.resources/getStringArray(org.jetbrains.compose.resources/ResourceEnvironment, org.jetbrains.compose.resources/StringArrayResource): kotlin.collections/List<kotlin/String> // org.jetbrains.compose.resources/getStringArray|getStringArray(org.jetbrains.compose.resources.ResourceEnvironment;org.jetbrains.compose.resources.StringArrayResource){}[0]
final suspend fun org.jetbrains.compose.resources/getStringArray(org.jetbrains.compose.resources/StringArrayResource): kotlin.collections/List<kotlin/String> // org.jetbrains.compose.resources/getStringArray|getStringArray(org.jetbrains.compose.resources.StringArrayResource){}[0]
open annotation class org.jetbrains.compose.resources/ExperimentalResourceApi : kotlin/Annotation { // org.jetbrains.compose.resources/ExperimentalResourceApi|null[0]
constructor <init>() // org.jetbrains.compose.resources/ExperimentalResourceApi.<init>|<init>(){}[0]
}
open annotation class org.jetbrains.compose.resources/InternalResourceApi : kotlin/Annotation { // org.jetbrains.compose.resources/InternalResourceApi|null[0]
constructor <init>() // org.jetbrains.compose.resources/InternalResourceApi.<init>|<init>(){}[0]
}
sealed class org.jetbrains.compose.resources/Resource { // org.jetbrains.compose.resources/Resource|null[0]
open fun equals(kotlin/Any?): kotlin/Boolean // org.jetbrains.compose.resources/Resource.equals|equals(kotlin.Any?){}[0]
open fun hashCode(): kotlin/Int // org.jetbrains.compose.resources/Resource.hashCode|hashCode(){}[0]
}
// Targets: [js, wasmJs]
final const val org.jetbrains.compose.resources.vector.xmldom/org_jetbrains_compose_resources_vector_xmldom_ElementImpl$stableprop // org.jetbrains.compose.resources.vector.xmldom/org_jetbrains_compose_resources_vector_xmldom_ElementImpl$stableprop|#static{}org_jetbrains_compose_resources_vector_xmldom_ElementImpl$stableprop[0]
// Targets: [js, wasmJs]
final const val org.jetbrains.compose.resources.vector.xmldom/org_jetbrains_compose_resources_vector_xmldom_NodeImpl$stableprop // org.jetbrains.compose.resources.vector.xmldom/org_jetbrains_compose_resources_vector_xmldom_NodeImpl$stableprop|#static{}org_jetbrains_compose_resources_vector_xmldom_NodeImpl$stableprop[0]
// Targets: [js, wasmJs]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_Intl_Locale$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_Intl_Locale$stableprop|#static{}org_jetbrains_compose_resources_Intl_Locale$stableprop[0]
// Targets: [js, wasmJs]
final const val org.jetbrains.compose.resources/org_jetbrains_compose_resources_WebResourcesConfiguration$stableprop // org.jetbrains.compose.resources/org_jetbrains_compose_resources_WebResourcesConfiguration$stableprop|#static{}org_jetbrains_compose_resources_WebResourcesConfiguration$stableprop[0]
// Targets: [js, wasmJs]
final fun org.jetbrains.compose.resources/configureWebResources(kotlin/Function1<org.jetbrains.compose.resources/WebResourcesConfiguration, kotlin/Unit>) // org.jetbrains.compose.resources/configureWebResources|configureWebResources(kotlin.Function1<org.jetbrains.compose.resources.WebResourcesConfiguration,kotlin.Unit>){}[0]
// Targets: [js, wasmJs]
final object org.jetbrains.compose.resources/WebResourcesConfiguration { // org.jetbrains.compose.resources/WebResourcesConfiguration|null[0]
final fun resourcePathMapping(kotlin/Function1<kotlin/String, kotlin/String>) // org.jetbrains.compose.resources/WebResourcesConfiguration.resourcePathMapping|resourcePathMapping(kotlin.Function1<kotlin.String,kotlin.String>){}[0]
}

12
components/resources/library/build.gradle.kts

@ -1,3 +1,4 @@
import kotlinx.validation.ExperimentalBCVApi
import org.jetbrains.compose.ExperimentalComposeLibrary import org.jetbrains.compose.ExperimentalComposeLibrary
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
@ -6,10 +7,9 @@ plugins {
id("org.jetbrains.compose") id("org.jetbrains.compose")
id("maven-publish") id("maven-publish")
id("com.android.library") id("com.android.library")
id("org.jetbrains.kotlinx.binary-compatibility-validator")
} }
val composeVersion = extra["compose.version"] as String
kotlin { kotlin {
jvm("desktop") jvm("desktop")
androidTarget { androidTarget {
@ -185,6 +185,7 @@ android {
assets.srcDir("src/androidInstrumentedTest/assets") assets.srcDir("src/androidInstrumentedTest/assets")
} }
named("test") { resources.srcDir(commonTestResources) } named("test") { resources.srcDir(commonTestResources) }
named("main") { manifest.srcFile("src/androidMain/AndroidManifest.xml") }
} }
} }
@ -194,9 +195,10 @@ configureMavenPublication(
name = "Resources for Compose JB" name = "Resources for Compose JB"
) )
// adding it here to make sure skiko is unpacked and available in web tests apiValidation {
compose.experimental { @OptIn(ExperimentalBCVApi::class)
web.application {} klib { enabled = true }
nonPublicMarkers.add("org.jetbrains.compose.resources.InternalResourceApi")
} }
//utility task to generate CLDRPluralRuleLists.kt file by 'CLDRPluralRules/plurals.xml' //utility task to generate CLDRPluralRuleLists.kt file by 'CLDRPluralRules/plurals.xml'

13
components/resources/library/src/androidMain/AndroidManifest.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<provider
android:authorities="${applicationId}.resources.AndroidContextProvider"
android:name="org.jetbrains.compose.resources.AndroidContextProvider"
android:exported="false"
android:enabled="true">
</provider>
</application>
</manifest>

81
components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/AndroidContextProvider.kt

@ -0,0 +1,81 @@
package org.jetbrains.compose.resources
import android.annotation.SuppressLint
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.pm.ProviderInfo
import android.database.Cursor
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
internal val androidContext get() = AndroidContextProvider.ANDROID_CONTEXT
/**
* The function configures the android context
* to be used for non-composable resource read functions
*
* e.g. `Res.readBytes(...)`
*
* Example usage:
* ```
* @Preview
* @Composable
* fun MyPreviewComponent() {
* PreviewContextConfigurationEffect()
* //...
* }
* ```
*/
@ExperimentalResourceApi
@Composable
fun PreviewContextConfigurationEffect() {
if (LocalInspectionMode.current) {
AndroidContextProvider.ANDROID_CONTEXT = LocalContext.current
}
}
//https://andretietz.com/2017/09/06/autoinitialise-android-library/
internal class AndroidContextProvider : ContentProvider() {
companion object {
@SuppressLint("StaticFieldLeak")
var ANDROID_CONTEXT: Context? = null
}
override fun onCreate(): Boolean {
ANDROID_CONTEXT = context
return true
}
override fun attachInfo(context: Context, info: ProviderInfo?) {
if (info == null) {
throw NullPointerException("AndroidContextProvider ProviderInfo cannot be null.")
}
// So if the authorities equal the library internal ones, the developer forgot to set his applicationId
if ("org.jetbrains.compose.components.resources.resources.AndroidContextProvider" == info.authority) {
throw IllegalStateException("Incorrect provider authority in manifest. Most likely due to a "
+ "missing applicationId variable your application\'s build.gradle.")
}
super.attachInfo(context, info)
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? = null
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int = 0
}

5
components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/FontResources.android.kt

@ -8,6 +8,7 @@ import androidx.compose.ui.text.font.*
@Composable @Composable
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font { actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font {
val environment = LocalComposeEnvironment.current.rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val path = remember(environment) { resource.getResourceItemByEnvironment(environment).path } val path = remember(environment, resource) { resource.getResourceItemByEnvironment(environment).path }
return Font(path, LocalContext.current.assets, weight, style) val assets = LocalContext.current.assets
return Font(path, assets, weight, style)
} }

13
components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ImageResources.android.kt

@ -6,8 +6,17 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
internal actual fun ByteArray.toImageBitmap(): ImageBitmap = internal actual fun ByteArray.toImageBitmap(resourceDensity: Int, targetDensity: Int): ImageBitmap {
BitmapFactory.decodeByteArray(this, 0, size).asImageBitmap() val options = BitmapFactory.Options().apply {
//https://youtrack.jetbrains.com/issue/CMP-5657
//android only downscales drawables. If there is only low dpi resource then use it as is (not upscale)
if (resourceDensity > targetDensity) {
inDensity = resourceDensity
inTargetDensity = targetDensity
}
}
return BitmapFactory.decodeByteArray(this, 0, size, options).asImageBitmap()
}
internal actual class SvgElement internal actual class SvgElement

93
components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.android.kt

@ -1,51 +1,96 @@
package org.jetbrains.compose.resources package org.jetbrains.compose.resources
import java.io.File import android.content.res.AssetManager
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import java.io.FileNotFoundException
import java.io.InputStream import java.io.InputStream
internal actual fun getPlatformResourceReader(): ResourceReader = object : ResourceReader { internal actual fun getPlatformResourceReader(): ResourceReader = object : ResourceReader {
private val assets: AssetManager by lazy {
val context = androidContext ?: error(
"Android context is not initialized. " +
"If it happens in the Preview mode then call PreviewContextConfigurationEffect() function."
)
context.assets
}
override suspend fun read(path: String): ByteArray { override suspend fun read(path: String): ByteArray {
val resource = getResourceAsStream(path) val resource = getResourceAsStream(path)
return resource.readBytes() return resource.use { input -> input.readBytes() }
} }
override suspend fun readPart(path: String, offset: Long, size: Long): ByteArray { override suspend fun readPart(path: String, offset: Long, size: Long): ByteArray {
val resource = getResourceAsStream(path) val resource = getResourceAsStream(path)
val result = ByteArray(size.toInt()) val result = ByteArray(size.toInt())
resource.use { input -> resource.use { input ->
input.skip(offset) input.skipBytes(offset)
input.read(result, 0, size.toInt()) input.readBytes(result, 0, size.toInt())
} }
return result return result
} }
//skipNBytes requires API 34
private fun InputStream.skipBytes(offset: Long) {
var skippedBytes = 0L
while (skippedBytes < offset) {
val count = skip(offset - skippedBytes)
if (count == 0L) break
skippedBytes += count
}
}
//readNBytes requires API 34
private fun InputStream.readBytes(byteArray: ByteArray, offset: Int, size: Int) {
var readBytes = 0
while (readBytes < size) {
val count = read(byteArray, offset + readBytes, size - readBytes)
if (count <= 0) break
readBytes += count
}
}
override fun getUri(path: String): String { override fun getUri(path: String): String {
val classLoader = getClassLoader() val uri = if (assets.hasFile(path)) {
val resource = classLoader.getResource(path) ?: run { Uri.parse("file:///android_asset/$path")
//try to find a font in the android assets } else {
if (File(path).isFontResource()) { val classLoader = getClassLoader()
classLoader.getResource("assets/$path") val resource = classLoader.getResource(path) ?: throw MissingResourceException(path)
} else null resource.toURI()
} ?: throw MissingResourceException(path) }
return resource.toURI().toString() return uri.toString()
} }
private fun getResourceAsStream(path: String): InputStream { private fun getResourceAsStream(path: String): InputStream {
val classLoader = getClassLoader() return try {
val resource = classLoader.getResourceAsStream(path) ?: run { assets.open(path)
//try to find a font in the android assets } catch (e: FileNotFoundException) {
if (File(path).isFontResource()) { val classLoader = getClassLoader()
classLoader.getResourceAsStream("assets/$path") classLoader.getResourceAsStream(path) ?: throw MissingResourceException(path)
} else null }
} ?: throw MissingResourceException(path)
return resource
} }
private fun File.isFontResource(): Boolean { private fun getClassLoader(): ClassLoader {
return this.parentFile?.name.orEmpty().startsWith("font") return this.javaClass.classLoader ?: error("Cannot find class loader")
} }
private fun getClassLoader(): ClassLoader { private fun AssetManager.hasFile(path: String): Boolean {
return Thread.currentThread().contextClassLoader ?: this.javaClass.classLoader!! var inputStream: InputStream? = null
val result = try {
inputStream = open(path)
true
} catch (e: FileNotFoundException) {
false
} finally {
inputStream?.close()
}
return result
} }
} }
internal actual val ProvidableCompositionLocal<ResourceReader>.currentOrPreview: ResourceReader
@Composable get() {
PreviewContextConfigurationEffect()
return current
}

30
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ImageResources.kt

@ -55,11 +55,18 @@ private val emptyImageBitmap: ImageBitmap by lazy { ImageBitmap(1, 1) }
*/ */
@Composable @Composable
fun imageResource(resource: DrawableResource): ImageBitmap { fun imageResource(resource: DrawableResource): ImageBitmap {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
val imageBitmap by rememberResourceState(resource, resourceReader, { emptyImageBitmap }) { env -> val resourceEnvironment = rememberResourceEnvironment()
val path = resource.getResourceItemByEnvironment(env).path val imageBitmap by rememberResourceState(
val cached = loadImage(path, resourceReader) { resource, resourceReader, resourceEnvironment, { emptyImageBitmap }
ImageCache.Bitmap(it.toImageBitmap()) ) { env ->
val item = resource.getResourceItemByEnvironment(env)
val resourceDensityQualifier = item.qualifiers.firstOrNull { it is DensityQualifier } as? DensityQualifier
val resourceDensity = resourceDensityQualifier?.dpi ?: DensityQualifier.MDPI.dpi
val screenDensity = resourceEnvironment.density.dpi
val path = item.path
val cached = loadImage(path, "$path-${screenDensity}dpi", resourceReader) {
ImageCache.Bitmap(it.toImageBitmap(resourceDensity, screenDensity))
} as ImageCache.Bitmap } as ImageCache.Bitmap
cached.bitmap cached.bitmap
} }
@ -78,11 +85,11 @@ private val emptyImageVector: ImageVector by lazy {
*/ */
@Composable @Composable
fun vectorResource(resource: DrawableResource): ImageVector { fun vectorResource(resource: DrawableResource): ImageVector {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
val density = LocalDensity.current val density = LocalDensity.current
val imageVector by rememberResourceState(resource, resourceReader, density, { emptyImageVector }) { env -> val imageVector by rememberResourceState(resource, resourceReader, density, { emptyImageVector }) { env ->
val path = resource.getResourceItemByEnvironment(env).path val path = resource.getResourceItemByEnvironment(env).path
val cached = loadImage(path, resourceReader) { val cached = loadImage(path, path, resourceReader) {
ImageCache.Vector(it.toXmlElement().toImageVector(density)) ImageCache.Vector(it.toXmlElement().toImageVector(density))
} as ImageCache.Vector } as ImageCache.Vector
cached.vector cached.vector
@ -98,11 +105,11 @@ private val emptySvgPainter: Painter by lazy { BitmapPainter(emptyImageBitmap) }
@Composable @Composable
private fun svgPainter(resource: DrawableResource): Painter { private fun svgPainter(resource: DrawableResource): Painter {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
val density = LocalDensity.current val density = LocalDensity.current
val svgPainter by rememberResourceState(resource, resourceReader, density, { emptySvgPainter }) { env -> val svgPainter by rememberResourceState(resource, resourceReader, density, { emptySvgPainter }) { env ->
val path = resource.getResourceItemByEnvironment(env).path val path = resource.getResourceItemByEnvironment(env).path
val cached = loadImage(path, resourceReader) { val cached = loadImage(path, path, resourceReader) {
ImageCache.Svg(it.toSvgElement().toSvgPainter(density)) ImageCache.Svg(it.toSvgElement().toSvgPainter(density))
} as ImageCache.Svg } as ImageCache.Svg
cached.painter cached.painter
@ -126,7 +133,7 @@ suspend fun getDrawableResourceBytes(
return DefaultResourceReader.read(resourceItem.path) return DefaultResourceReader.read(resourceItem.path)
} }
internal expect fun ByteArray.toImageBitmap(): ImageBitmap internal expect fun ByteArray.toImageBitmap(resourceDensity: Int, targetDensity: Int): ImageBitmap
internal expect fun ByteArray.toXmlElement(): Element internal expect fun ByteArray.toXmlElement(): Element
internal expect fun ByteArray.toSvgElement(): SvgElement internal expect fun ByteArray.toSvgElement(): SvgElement
@ -145,6 +152,7 @@ internal fun dropImageCache() {
private suspend fun loadImage( private suspend fun loadImage(
path: String, path: String,
cacheKey: String,
resourceReader: ResourceReader, resourceReader: ResourceReader,
decode: (ByteArray) -> ImageCache decode: (ByteArray) -> ImageCache
): ImageCache = imageCache.getOrLoad(path) { decode(resourceReader.read(path)) } ): ImageCache = imageCache.getOrLoad(cacheKey) { decode(resourceReader.read(path)) }

4
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/PluralStringResources.kt

@ -26,7 +26,7 @@ class PluralStringResource
*/ */
@Composable @Composable
fun pluralStringResource(resource: PluralStringResource, quantity: Int): String { fun pluralStringResource(resource: PluralStringResource, quantity: Int): String {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
val pluralStr by rememberResourceState(resource, quantity, { "" }) { env -> val pluralStr by rememberResourceState(resource, quantity, { "" }) { env ->
loadPluralString(resource, quantity, resourceReader, env) loadPluralString(resource, quantity, resourceReader, env)
} }
@ -93,7 +93,7 @@ private suspend fun loadPluralString(
*/ */
@Composable @Composable
fun pluralStringResource(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String { fun pluralStringResource(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
val args = formatArgs.map { it.toString() } val args = formatArgs.map { it.toString() }
val pluralStr by rememberResourceState(resource, quantity, args, { "" }) { env -> val pluralStr by rememberResourceState(resource, quantity, args, { "" }) { env ->
loadPluralString(resource, quantity, args, resourceReader, env) loadPluralString(resource, quantity, args, resourceReader, env)

42
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/Qualifier.kt

@ -3,14 +3,48 @@ package org.jetbrains.compose.resources
interface Qualifier interface Qualifier
@InternalResourceApi @InternalResourceApi
data class LanguageQualifier( class LanguageQualifier(
val language: String val language: String
) : Qualifier ) : Qualifier {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as LanguageQualifier
return language == other.language
}
override fun hashCode(): Int {
return language.hashCode()
}
override fun toString(): String {
return "LanguageQualifier(language='$language')"
}
}
@InternalResourceApi @InternalResourceApi
data class RegionQualifier( class RegionQualifier(
val region: String val region: String
) : Qualifier ) : Qualifier {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as RegionQualifier
return region == other.region
}
override fun hashCode(): Int {
return region.hashCode()
}
override fun toString(): String {
return "RegionQualifier(region='$region')"
}
}
@InternalResourceApi @InternalResourceApi
enum class ThemeQualifier : Qualifier { enum class ThemeQualifier : Qualifier {

51
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceEnvironment.kt

@ -97,7 +97,7 @@ internal fun Resource.getResourceItemByEnvironment(environment: ResourceEnvironm
.also { if (it.size == 1) return it.first() } .also { if (it.size == 1) return it.first() }
.filterBy(environment.theme) .filterBy(environment.theme)
.also { if (it.size == 1) return it.first() } .also { if (it.size == 1) return it.first() }
.filterBy(environment.density) .filterByDensity(environment.density)
.also { if (it.size == 1) return it.first() } .also { if (it.size == 1) return it.first() }
.let { items -> .let { items ->
if (items.isEmpty()) { if (items.isEmpty()) {
@ -125,12 +125,59 @@ private fun List<ResourceItem>.filterBy(qualifier: Qualifier): List<ResourceItem
} }
} }
// https://developer.android.com/guide/topics/resources/providing-resources#BestMatch
// In general, Android prefers scaling down a larger original image to scaling up a smaller original image.
private fun List<ResourceItem>.filterByDensity(density: DensityQualifier): List<ResourceItem> {
val items = this
var withQualifier = emptyList<ResourceItem>()
// filter with the same or better density
val exactAndHigherQualifiers = DensityQualifier.entries
.filter { it.dpi >= density.dpi }
.sortedBy { it.dpi }
for (qualifier in exactAndHigherQualifiers) {
withQualifier = items.filter { item -> item.qualifiers.any { it == qualifier } }
if (withQualifier.isNotEmpty()) break
}
if (withQualifier.isNotEmpty()) return withQualifier
// filter with low density
val lowQualifiers = DensityQualifier.entries
.minus(DensityQualifier.LDPI)
.filter { it.dpi < density.dpi }
.sortedByDescending { it.dpi }
for (qualifier in lowQualifiers) {
withQualifier = items.filter { item -> item.qualifiers.any { it == qualifier } }
if (withQualifier.isNotEmpty()) break
}
if (withQualifier.isNotEmpty()) return withQualifier
//items with no DensityQualifier (default)
// The system assumes that default resources (those from a directory without configuration qualifiers)
// are designed for the baseline pixel density (mdpi) and resizes those bitmaps
// to the appropriate size for the current pixel density.
// https://developer.android.com/training/multiscreen/screendensities#DensityConsiderations
val withNoDensity = items.filter { item ->
item.qualifiers.none { it is DensityQualifier }
}
if (withNoDensity.isNotEmpty()) return withNoDensity
//items with LDPI density
return items.filter { item ->
item.qualifiers.any { it == DensityQualifier.LDPI }
}
}
// we need to filter by language and region together because there is slightly different logic: // we need to filter by language and region together because there is slightly different logic:
// 1) if there is the exact match language+region then use it // 1) if there is the exact match language+region then use it
// 2) if there is the language WITHOUT region match then use it // 2) if there is the language WITHOUT region match then use it
// 3) in other cases use items WITHOUT language and region qualifiers at all // 3) in other cases use items WITHOUT language and region qualifiers at all
// issue: https://github.com/JetBrains/compose-multiplatform/issues/4571 // issue: https://github.com/JetBrains/compose-multiplatform/issues/4571
private fun List<ResourceItem>.filterByLocale(language: LanguageQualifier, region: RegionQualifier): List<ResourceItem> { private fun List<ResourceItem>.filterByLocale(
language: LanguageQualifier,
region: RegionQualifier
): List<ResourceItem> {
val withLanguage = filter { item -> val withLanguage = filter { item ->
item.qualifiers.any { it == language } item.qualifiers.any { it == language }
} }

6
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceReader.kt

@ -1,5 +1,7 @@
package org.jetbrains.compose.resources package org.jetbrains.compose.resources
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf
class MissingResourceException(path: String) : Exception("Missing resource with path: $path") class MissingResourceException(path: String) : Exception("Missing resource with path: $path")
@ -34,3 +36,7 @@ internal val DefaultResourceReader = getPlatformResourceReader()
//ResourceReader provider will be overridden for tests //ResourceReader provider will be overridden for tests
internal val LocalResourceReader = staticCompositionLocalOf { DefaultResourceReader } internal val LocalResourceReader = staticCompositionLocalOf { DefaultResourceReader }
//For an android preview we need to initialize the resource reader with the local context
internal expect val ProvidableCompositionLocal<ResourceReader>.currentOrPreview: ResourceReader
@Composable get

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

@ -30,7 +30,7 @@ class StringArrayResource
*/ */
@Composable @Composable
fun stringArrayResource(resource: StringArrayResource): List<String> { fun stringArrayResource(resource: StringArrayResource): List<String> {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
val array by rememberResourceState(resource, { emptyList() }) { env -> val array by rememberResourceState(resource, { emptyList() }) { env ->
loadStringArray(resource, resourceReader, env) loadStringArray(resource, resourceReader, env)
} }

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

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

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

@ -59,6 +59,44 @@ class ComposeResourceTest {
) )
} }
@Test
fun testImageResourceDensity() = runComposeUiTest {
val testResourceReader = TestResourceReader()
val imgRes = DrawableResource(
"test_id", setOf(
ResourceItem(setOf(DensityQualifier.XXXHDPI), "2.png", -1, -1),
ResourceItem(setOf(DensityQualifier.MDPI), "1.png", -1, -1),
)
)
val mdpiEnvironment = object : ComposeEnvironment {
@Composable
override fun rememberEnvironment() = ResourceEnvironment(
language = LanguageQualifier("en"),
region = RegionQualifier("US"),
theme = ThemeQualifier.LIGHT,
density = DensityQualifier.MDPI
)
}
var environment by mutableStateOf(TestComposeEnvironment)
setContent {
CompositionLocalProvider(
LocalResourceReader provides testResourceReader,
LocalComposeEnvironment provides environment
) {
Image(painterResource(imgRes), null)
}
}
waitForIdle()
environment = mdpiEnvironment
waitForIdle()
assertEquals(
expected = listOf("2.png", "1.png"), //XXXHDPI - fist, MDPI - next
actual = testResourceReader.readPaths
)
}
@Test @Test
fun testStringResourceCache() = runComposeUiTest { fun testStringResourceCache() = runComposeUiTest {
val testResourceReader = TestResourceReader() val testResourceReader = TestResourceReader()
@ -293,7 +331,7 @@ class ComposeResourceTest {
var uri2 = "" var uri2 = ""
setContent { setContent {
CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) { CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.currentOrPreview
uri1 = resourceReader.getUri("1.png") uri1 = resourceReader.getUri("1.png")
uri2 = resourceReader.getUri("2.png") uri2 = resourceReader.getUri("2.png")
} }

8
components/resources/library/src/desktopMain/kotlin/org/jetbrains/compose/resources/ResourceReader.desktop.kt

@ -5,15 +5,15 @@ import java.io.InputStream
internal actual fun getPlatformResourceReader(): ResourceReader = object : ResourceReader { internal actual fun getPlatformResourceReader(): ResourceReader = object : ResourceReader {
override suspend fun read(path: String): ByteArray { override suspend fun read(path: String): ByteArray {
val resource = getResourceAsStream(path) val resource = getResourceAsStream(path)
return resource.readBytes() return resource.use { input -> input.readBytes() }
} }
override suspend fun readPart(path: String, offset: Long, size: Long): ByteArray { override suspend fun readPart(path: String, offset: Long, size: Long): ByteArray {
val resource = getResourceAsStream(path) val resource = getResourceAsStream(path)
val result = ByteArray(size.toInt()) val result = ByteArray(size.toInt())
resource.use { input -> resource.use { input ->
input.skip(offset) input.skipNBytes(offset)
input.read(result, 0, size.toInt()) input.readNBytes(result, 0, size.toInt())
} }
return result return result
} }
@ -30,6 +30,6 @@ internal actual fun getPlatformResourceReader(): ResourceReader = object : Resou
} }
private fun getClassLoader(): ClassLoader { private fun getClassLoader(): ClassLoader {
return Thread.currentThread().contextClassLoader ?: this.javaClass.classLoader!! return this.javaClass.classLoader ?: error("Cannot find class loader")
} }
} }

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

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

31
components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/ImageResources.skiko.kt

@ -6,10 +6,37 @@ import androidx.compose.ui.graphics.toComposeImageBitmap
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
import org.jetbrains.skia.Data import org.jetbrains.skia.Data
import org.jetbrains.skia.Image import org.jetbrains.skia.Image
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect
import org.jetbrains.skia.SamplingMode
import org.jetbrains.skia.Surface
import org.jetbrains.skia.svg.SVGDOM import org.jetbrains.skia.svg.SVGDOM
internal actual fun ByteArray.toImageBitmap(): ImageBitmap = internal actual fun ByteArray.toImageBitmap(resourceDensity: Int, targetDensity: Int): ImageBitmap {
Image.makeFromEncoded(this).toComposeImageBitmap() val image = Image.makeFromEncoded(this)
val targetImage: Image
//https://youtrack.jetbrains.com/issue/CMP-5657
//android only downscales drawables. If there is only low dpi resource then use it as is (not upscale)
//we need a consistent behavior on all platforms
if (resourceDensity > targetDensity) {
val scale = targetDensity.toFloat() / resourceDensity.toFloat()
val targetH = image.height * scale
val targetW = image.width * scale
val srcRect = Rect.Companion.makeWH(image.width.toFloat(), image.height.toFloat())
val dstRect = Rect.Companion.makeWH(targetW, targetH)
targetImage = Surface.makeRasterN32Premul(targetW.toInt(), targetH.toInt()).run {
val paint = Paint().apply { isAntiAlias = true }
canvas.drawImageRect(image, srcRect, dstRect, SamplingMode.LINEAR, paint, true)
makeImageSnapshot()
}
} else {
targetImage = image
}
return targetImage.toComposeImageBitmap()
}
internal actual class SvgElement(val svgdom: SVGDOM) internal actual class SvgElement(val svgdom: SVGDOM)

7
components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/ResourceReader.skiko.kt

@ -0,0 +1,7 @@
package org.jetbrains.compose.resources
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
internal actual val ProvidableCompositionLocal<ResourceReader>.currentOrPreview: ResourceReader
@Composable get() = current

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

@ -1,10 +1,12 @@
package org.jetbrains.compose.resources package org.jetbrains.compose.resources
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.launch
@Composable @Composable
internal actual fun <T> rememberResourceState( internal actual fun <T> rememberResourceState(
@ -13,11 +15,14 @@ internal actual fun <T> rememberResourceState(
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1) { mutableStateOf(getDefault()) } val scope = rememberCoroutineScope()
LaunchedEffect(key1) { return remember(key1) {
state.value = block(environment) val mutableState = mutableStateOf(getDefault())
scope.launch(start = CoroutineStart.UNDISPATCHED) {
mutableState.value = block(environment)
}
mutableState
} }
return state
} }
@Composable @Composable
@ -28,11 +33,14 @@ internal actual fun <T> rememberResourceState(
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1, key2) { mutableStateOf(getDefault()) } val scope = rememberCoroutineScope()
LaunchedEffect(key1, key2) { return remember(key1, key2) {
state.value = block(environment) val mutableState = mutableStateOf(getDefault())
scope.launch(start = CoroutineStart.UNDISPATCHED) {
mutableState.value = block(environment)
}
mutableState
} }
return state
} }
@Composable @Composable
@ -44,9 +52,12 @@ internal actual fun <T> rememberResourceState(
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1, key2, key3) { mutableStateOf(getDefault()) } val scope = rememberCoroutineScope()
LaunchedEffect(key1, key2, key3) { return remember(key1, key2, key3) {
state.value = block(environment) val mutableState = mutableStateOf(getDefault())
scope.launch(start = CoroutineStart.UNDISPATCHED) {
mutableState.value = block(environment)
}
mutableState
} }
return state
} }

6
components/settings.gradle.kts

@ -14,6 +14,12 @@ pluginManagement {
kotlin("multiplatform").version(extra["kotlin.version"] as String) kotlin("multiplatform").version(extra["kotlin.version"] as String)
id("org.jetbrains.compose").version(extra["compose.version"] as String) id("org.jetbrains.compose").version(extra["compose.version"] as String)
id("com.android.library").version(extra["agp.version"] as String) id("com.android.library").version(extra["agp.version"] as String)
id("org.jetbrains.kotlinx.binary-compatibility-validator").version("0.15.0-Beta.2")
}
val gradlePluginDir = rootDir.resolve("../gradle-plugins")
if (gradlePluginDir.exists()) {
includeBuild(gradlePluginDir)
} }
} }

2
components/ui-tooling-preview/library/build.gradle.kts

@ -7,8 +7,6 @@ plugins {
id("com.android.library") id("com.android.library")
} }
val composeVersion = extra["compose.version"] as String
kotlin { kotlin {
jvm("desktop") jvm("desktop")
androidTarget { androidTarget {

2
examples/chat/iosApp/iosApp/Info.plist

@ -20,6 +20,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

2
examples/codeviewer/iosApp/iosApp/Info.plist

@ -20,6 +20,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

2
examples/graphics-2d/iosApp/iosApp/Info.plist

@ -20,6 +20,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

2
examples/imageviewer/iosApp/iosApp/Info.plist

@ -22,6 +22,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>This app uses camera for capturing photos</string> <string>This app uses camera for capturing photos</string>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>

7
examples/imageviewer/shared/src/androidMain/AndroidManifest.xml

@ -10,6 +10,11 @@
android:name="example.imageviewer.ImageViewerFileProvider" android:name="example.imageviewer.ImageViewerFileProvider"
android:authorities="example.imageviewer.fileprovider" android:authorities="example.imageviewer.fileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"></provider> android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application> </application>
</manifest> </manifest>

1
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/Dependencies.kt

@ -39,6 +39,7 @@ abstract class Dependencies {
} }
override fun saveImage(picture: PictureData.Camera, image: PlatformStorableImage) { override fun saveImage(picture: PictureData.Camera, image: PlatformStorableImage) {
pictures.add(0, picture)
imageStorage.saveImage(picture, image) imageStorage.saveImage(picture, image)
} }

18
examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/DesktopImageStorage.kt

@ -1,6 +1,5 @@
package example.imageviewer package example.imageviewer
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toAwtImage import androidx.compose.ui.graphics.toAwtImage
import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap
@ -13,25 +12,24 @@ private const val maxStorableImageSizePx = 2000
private const val storableThumbnailSizePx = 200 private const val storableThumbnailSizePx = 200
class DesktopImageStorage( class DesktopImageStorage(
private val pictures: SnapshotStateList<PictureData>,
private val ioScope: CoroutineScope private val ioScope: CoroutineScope
) : ImageStorage { ) : ImageStorage {
private val largeImages = mutableMapOf<PictureData.Camera, ImageBitmap>() private val largeImages = mutableMapOf<String, ImageBitmap>()
private val thumbnails = mutableMapOf<PictureData.Camera, ImageBitmap>() private val thumbnails = mutableMapOf<String, ImageBitmap>()
override fun saveImage(picture: PictureData.Camera, image: PlatformStorableImage) { override fun saveImage(picture: PictureData.Camera, image: PlatformStorableImage) {
if (image.imageBitmap.width == 0 || image.imageBitmap.height == 0) { if (image.imageBitmap.width == 0 || image.imageBitmap.height == 0) {
return return
} }
ioScope.launch { ioScope.launch {
largeImages[picture] = image.imageBitmap.fitInto(maxStorableImageSizePx) largeImages[picture.id] = image.imageBitmap.fitInto(maxStorableImageSizePx)
thumbnails[picture] = image.imageBitmap.fitInto(storableThumbnailSizePx) thumbnails[picture.id] = image.imageBitmap.fitInto(storableThumbnailSizePx)
pictures.add(0, picture)
} }
} }
override fun delete(picture: PictureData.Camera) { override fun delete(picture: PictureData.Camera) {
// For now, on Desktop pictures saving in memory. We don't need additional delete logic. largeImages.remove(picture.id)
thumbnails.remove(picture.id)
} }
override fun rewrite(picture: PictureData.Camera) { override fun rewrite(picture: PictureData.Camera) {
@ -39,11 +37,11 @@ class DesktopImageStorage(
} }
override suspend fun getThumbnail(picture: PictureData.Camera): ImageBitmap { override suspend fun getThumbnail(picture: PictureData.Camera): ImageBitmap {
return thumbnails[picture]!! return thumbnails[picture.id]!!
} }
override suspend fun getImage(picture: PictureData.Camera): ImageBitmap { override suspend fun getImage(picture: PictureData.Camera): ImageBitmap {
return largeImages[picture]!! return largeImages[picture.id]!!
} }
} }

2
examples/imageviewer/shared/src/desktopMain/kotlin/example/imageviewer/view/ImageViewer.desktop.kt

@ -104,7 +104,7 @@ private fun getDependencies(
toastState.value = ToastState.Shown(text) toastState.value = ToastState.Shown(text)
} }
} }
override val imageStorage: DesktopImageStorage = DesktopImageStorage(pictures, ioScope) override val imageStorage: DesktopImageStorage = DesktopImageStorage(ioScope)
override val sharePicture: SharePicture = object : SharePicture { override val sharePicture: SharePicture = object : SharePicture {
override fun share(context: PlatformContext, picture: PictureData) { override fun share(context: PlatformContext, picture: PictureData) {
// On Desktop share feature not supported // On Desktop share feature not supported

12
examples/imageviewer/shared/src/desktopTest/kotlin/ImageViewerTest.kt

@ -1,7 +1,12 @@
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performClick
import example.imageviewer.* import example.imageviewer.Dependencies
import example.imageviewer.DesktopImageStorage
import example.imageviewer.ImageViewerCommon
import example.imageviewer.Notification
import example.imageviewer.PopupNotification
import example.imageviewer.SharePicture
import example.imageviewer.filter.PlatformContext import example.imageviewer.filter.PlatformContext
import example.imageviewer.model.PictureData import example.imageviewer.model.PictureData
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -18,9 +23,10 @@ class ImageViewerTest {
override fun showPopUpMessage(text: String) { override fun showPopUpMessage(text: String) {
} }
} }
override val imageStorage: DesktopImageStorage = DesktopImageStorage(pictures, CoroutineScope(Dispatchers.Main)) override val imageStorage: DesktopImageStorage =
DesktopImageStorage(CoroutineScope(Dispatchers.Main))
override val sharePicture: SharePicture = object : SharePicture { override val sharePicture: SharePicture = object : SharePicture {
override fun share(context: PlatformContext, picture: PictureData) { } override fun share(context: PlatformContext, picture: PictureData) {}
} }
} }

33
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt

@ -52,22 +52,23 @@ fun getDependencies(ioScope: CoroutineScope, toastState: MutableState<ToastState
override val sharePicture: SharePicture = object : SharePicture { override val sharePicture: SharePicture = object : SharePicture {
override fun share(context: PlatformContext, picture: PictureData) { override fun share(context: PlatformContext, picture: PictureData) {
ioScope.launch { ioScope.launch {
val data = imageStorage.getNSDataToShare(picture) imageStorage.getNSURLToShare(picture).path?.let { imageUrl ->
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val window = UIApplication.sharedApplication.windows.last() as? UIWindow val window = UIApplication.sharedApplication.windows.last() as? UIWindow
val currentViewController = window?.rootViewController val currentViewController = window?.rootViewController
val activityViewController = UIActivityViewController( val activityViewController = UIActivityViewController(
activityItems = listOf( activityItems = listOf(
UIImage(data = data), UIImage.imageWithContentsOfFile(imageUrl),
picture.description picture.description
), ),
applicationActivities = null applicationActivities = null
) )
currentViewController?.presentViewController( currentViewController?.presentViewController(
viewControllerToPresent = activityViewController, viewControllerToPresent = activityViewController,
animated = true, animated = true,
completion = null, completion = null,
) )
}
} }
} }
} }

19
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/storage/IosImageStorage.ios.kt

@ -14,7 +14,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import platform.CoreGraphics.CGRectMake import platform.CoreGraphics.CGRectMake
@ -36,7 +35,7 @@ private const val storableThumbnailSizePx = 180
private const val jpegCompressionQuality = 60 private const val jpegCompressionQuality = 60
class IosImageStorage( class IosImageStorage(
private val pictures: SnapshotStateList<PictureData>, pictures: SnapshotStateList<PictureData>,
private val ioScope: CoroutineScope private val ioScope: CoroutineScope
) : ImageStorage { ) : ImageStorage {
@ -77,7 +76,6 @@ class IosImageStorage(
picture.jpgFile.writeJpeg(fitInto(maxStorableImageSizePx)) picture.jpgFile.writeJpeg(fitInto(maxStorableImageSizePx))
picture.thumbnailJpgFile.writeJpeg(fitInto(storableThumbnailSizePx)) picture.thumbnailJpgFile.writeJpeg(fitInto(storableThumbnailSizePx))
} }
pictures.add(0, picture)
picture.jsonFile.writeText(picture.toJson()) picture.jsonFile.writeText(picture.toJson())
} }
} }
@ -121,6 +119,21 @@ class IosImageStorage(
} }
}.readData() }.readData()
} }
suspend fun getNSURLToShare(picture: PictureData): NSURL = withContext(Dispatchers.IO) {
when (picture) {
is PictureData.Camera -> {
picture.jpgFile
}
is PictureData.Resource -> {
NSURL(
fileURLWithPath = NSBundle.mainBundle.resourcePath + "/" + picture.resource,
isDirectory = false
)
}
}
}
} }
@OptIn(ExperimentalForeignApi::class) @OptIn(ExperimentalForeignApi::class)

21
examples/imageviewer/webApp/build.gradle.kts

@ -26,16 +26,17 @@ kotlin {
wasmJs { wasmJs {
moduleName = "imageviewer" moduleName = "imageviewer"
browser { browser {
commonWebpackConfig { // TODO: uncomment when https://youtrack.jetbrains.com/issue/KT-68614 is fixed (it doesn't work with configuration cache)
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { // commonWebpackConfig {
static = (static ?: mutableListOf()).apply { // devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
// Serve sources to debug inside browser // static = (static ?: mutableListOf()).apply {
add(rootDirPath) // // Serve sources to debug inside browser
add(rootDirPath + "/shared/") // add(rootDirPath)
add(rootDirPath + "/webApp/") // add(rootDirPath + "/shared/")
} // add(rootDirPath + "/webApp/")
} // }
} // }
// }
} }
binaries.executable() binaries.executable()
} }

3
examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/ui/components/Button.kt

@ -86,11 +86,14 @@ fun JetsnackButton(
value = MaterialTheme.typography.button value = MaterialTheme.typography.button
) { ) {
Row( Row(
@Suppress("DEPRECATION_ERROR")
Modifier Modifier
.defaultMinSize( .defaultMinSize(
minWidth = ButtonDefaults.MinWidth, minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight minHeight = ButtonDefaults.MinHeight
) )
// TODO This should be replaced by non-deprecated alternative after the original example migrates to Jetpack Compose 1.7:
// https://github.com/android/compose-samples/blob/3bc6b7d7c74571ea74776ec5b15518b40de4d31b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Button.kt#L95
.indication(interactionSource, rememberRipple()) .indication(interactionSource, rememberRipple())
.padding(contentPadding), .padding(contentPadding),
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,

2
examples/jetsnack/ios/iosApp/Info.plist

@ -22,6 +22,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>This app uses camera for capturing photos</string> <string>This app uses camera for capturing photos</string>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>

2
examples/todoapp-lite/iosApp/iosApp/Info.plist

@ -20,6 +20,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

2
examples/widgets-gallery/iosApp/iosApp/Info.plist

@ -20,6 +20,8 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

3
experimental/examples/intellij-plugin-with-experimental-shared-base/.gitignore vendored

@ -1,3 +0,0 @@
build/
.gradle/
.idea/

23
experimental/examples/intellij-plugin-with-experimental-shared-base/.run/runIde.run.xml

@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="runIde" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="runIde" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
</component>

20
experimental/examples/intellij-plugin-with-experimental-shared-base/README.md

@ -1,20 +0,0 @@
## Example Compose Multiplatform based plugin for IntelliJ Idea.
A plugin, demonstrating an Intellij plugin, showing a dialog window written with Compose.
The only difference from [examples/intellij-plugin](../intellij-plugin) is that
this version does not bundle Compose runtime, which makes the plugin smaller
and allows sharing Compose runtime between multiple plugins
(Compose class files and native libraries are not loaded by each plugin).
### Usage
1. Start test IDE:
* Run the following command in terminal: `./gradlew runIde`
* Or choose **runIde** configuration in IDE and run it.
![ide-run-configuration.png](screenshots/ide-run-configuration.png)
2. Create a new project or open any existing;
3. Select `Show Compose Demo...` from the `Tools` menu.
![screen1](../intellij-plugin/screenshots/toolsshow.png)
![screen2](../intellij-plugin/screenshots/screenshot.png)

38
experimental/examples/intellij-plugin-with-experimental-shared-base/build.gradle.kts

@ -1,38 +0,0 @@
import org.jetbrains.compose.compose
plugins {
id("org.jetbrains.intellij") version "1.6.0"
java
kotlin("jvm")
id("org.jetbrains.compose")
id("idea")
}
group = "org.jetbrains.compose.intellij.platform"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
google()
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
}
dependencies {
// compileOnly(compose.desktop.currentOs) runtime dependency is provided by org.jetbrains.compose.intellij.platform
testImplementation(kotlin("test"))
}
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version.set("2021.3")
plugins.set(
listOf(
"org.jetbrains.compose.intellij.platform:0.1.0",
"org.jetbrains.kotlin"
)
)
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}

5
experimental/examples/intellij-plugin-with-experimental-shared-base/gradle.properties

@ -1,5 +0,0 @@
kotlin.stdlib.default.dependency=false
kotlin.code.style=official
kotlin.version=1.8.0
compose.version=1.3.0

BIN
experimental/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.jar vendored

Binary file not shown.

5
experimental/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.properties vendored

@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
experimental/examples/intellij-plugin-with-experimental-shared-base/gradlew vendored

@ -1,185 +0,0 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
experimental/examples/intellij-plugin-with-experimental-shared-base/gradlew.bat vendored

@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

BIN
experimental/examples/intellij-plugin-with-experimental-shared-base/screenshots/ide-run-configuration.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

8
experimental/examples/intellij-plugin-with-experimental-shared-base/settings.gradle.kts

@ -1,8 +0,0 @@
rootProject.name = "ComposeDemoPlugin"
pluginManagement {
plugins {
kotlin("jvm").version(extra["kotlin.version"] as String)
id("org.jetbrains.compose").version(extra["compose.version"] as String)
}
}

70
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/ComposeDemoAction.kt

@ -1,70 +0,0 @@
package com.jetbrains.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Surface
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.onGloballyPositioned
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.jetbrains.compose.theme.WidgetTheme
import com.jetbrains.compose.widgets.Buttons
import com.jetbrains.compose.widgets.LazyScrollable
import com.jetbrains.compose.widgets.Loaders
import com.jetbrains.compose.widgets.TextInputs
import com.jetbrains.compose.widgets.Toggles
import java.awt.Dimension
import javax.swing.JComponent
import javax.swing.SwingUtilities
/**
* @author Konstantin Bulenkov
*/
class ComposeDemoAction : DumbAwareAction() {
override fun actionPerformed(e: AnActionEvent) {
DemoDialog(e.project).show()
}
class DemoDialog(project: Project?) : DialogWrapper(project) {
init {
title = "Demo"
init()
}
override fun createCenterPanel(): JComponent {
return ComposePanel().apply {
setBounds(0, 0, 800, 600)
setContent {
WidgetTheme(darkTheme = true) {
Surface(modifier = Modifier.fillMaxSize()) {
Row {
Column(
modifier = Modifier.fillMaxHeight().weight(1f)
) {
Buttons()
Loaders()
TextInputs()
Toggles()
}
Box(
modifier = Modifier.fillMaxHeight().weight(1f)
) {
LazyScrollable()
}
}
}
}
}
}
}
}
}

28
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/IntellijTheme.kt

@ -1,28 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.intellij.openapi.project.Project
import com.jetbrains.compose.theme.WidgetTheme
import org.intellij.datavis.r.inlays.components.GraphicsManager
@Composable
fun IntellijTheme(project: Project, content: @Composable () -> Unit) {
val isDarkMode = try {
GraphicsManager.getInstance(project)?.isDarkModeEnabled ?: false
} catch (t: Throwable) {
false
}
WidgetTheme(darkTheme = isDarkMode) {
Surface(modifier = Modifier.fillMaxSize()) {
content()
}
}
}

108
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/color/ColorLineMarkerProvider.kt

@ -1,108 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.color
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.graphics.Color
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProvider
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.PsiElement
import com.jetbrains.compose.IntellijTheme
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.uast.*
import java.awt.Component
import java.awt.Graphics
import javax.swing.Icon
import javax.swing.JComponent
class ColorLineMarkerProvider : LineMarkerProvider {
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
val project = element.project
val ktPsiFactory = KtPsiFactory(project)
val uElement: UElement = element.toUElement() ?: return null
if (uElement is UCallExpression) {
if (uElement.kind == UastCallKind.METHOD_CALL && uElement.methodIdentifier?.name == "Color") {
val colorLongValue = (uElement.valueArguments.firstOrNull() as? ULiteralExpression)?.getLongValue()
val previousColor = try {
Color(colorLongValue!!)
} catch (t: Throwable) {
Color(0xffffffff)
}
val iconSize = 20
return LineMarkerInfo(
element,
element.textRange,
object : Icon {
override fun paintIcon(c: Component?, g: Graphics?, x: Int, y: Int) {
g?.color = java.awt.Color(
previousColor.red,
previousColor.green,
previousColor.blue,
previousColor.alpha
)
g?.fillRect(0, 0, iconSize, iconSize)
}
override fun getIconWidth(): Int = iconSize
override fun getIconHeight(): Int = iconSize
},
null,
{ _, psiElement: PsiElement ->
class ChooseColorDialog() : DialogWrapper(project) {
val colorState = mutableStateOf(previousColor)
init {
title = "Choose color"
init()
}
override fun createCenterPanel(): JComponent =
ComposePanel().apply {
setBounds(0, 0, 400, 400)
setContent {
IntellijTheme(project) {
ColorPicker(colorState)
}
}
}
}
val chooseColorDialog = ChooseColorDialog()
val result = chooseColorDialog.showAndGet()
if (result) {
val color = chooseColorDialog.colorState.value
ApplicationManager.getApplication().runWriteAction {
psiElement.replace(
ktPsiFactory.createExpression(
"Color(${color.toHexString()})"
)
)
}
}
},
GutterIconRenderer.Alignment.RIGHT,
{ "change color literal" }
)
}
}
return null
}
override fun collectSlowLineMarkers(
elements: MutableList<out PsiElement>,
result: MutableCollection<in LineMarkerInfo<*>>
) {
}
}

128
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/color/ColorPicker.kt

@ -1,128 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.color
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.isPrimaryPressed
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
private const val VALUE_BAND_RATIO = 0.07f
private val DEFAULT_COLORS =
listOf(Color.Red, Color.Green, Color.Blue, Color.Black, Color.Gray, Color.Yellow, Color.Cyan, Color.Magenta)
@Composable
fun ColorPicker(colorState: MutableState<Color>) {
var currentColor: Color by remember { colorState }
Column {
Row {
DEFAULT_COLORS.forEach {
Box(Modifier.size(30.dp).background(color = it).clickable {
currentColor = it
})
}
}
Divider(Modifier.size(2.dp))
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text("Result color:")
Divider(Modifier.size(2.dp))
TextField(modifier = Modifier.width(120f.dp), value = currentColor.toHexString(), onValueChange = {})
Divider(Modifier.size(2.dp))
val size = 60f
Box(Modifier.size(size.dp).background(color = currentColor))
}
Divider(Modifier.size(2.dp))
var width by remember { mutableStateOf(300) }
var height by remember { mutableStateOf(256) }
val rainbowWidth by derivedStateOf { (width * (1 - VALUE_BAND_RATIO)).toInt() }
val bandWidth by derivedStateOf { width * VALUE_BAND_RATIO }
fun calcHue(x: Float) = limit0to1(x / rainbowWidth) * HSV.HUE_MAX_VALUE
fun calcSaturation(y: Float) = 1 - limit0to1(y / height)
fun calcValue(y: Float) = 1 - limit0to1(y / height)
Row(Modifier.fillMaxSize()) {
Canvas(Modifier.fillMaxSize().pointerInput(Unit) {
width = size.width
height = size.height
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
if (event.buttons.isPrimaryPressed) {
val position = event.changes.first().position
if (position.x < rainbowWidth) {
currentColor = try {
currentColor.toHsv().copy(
hue = calcHue(position.x),
saturation = calcSaturation(position.y)
).toRgb()
} catch (t: Throwable) {
t.printStackTrace()
println("exception $t")
currentColor
}
} else {
currentColor =
currentColor.toHsv().copy(
value = calcValue(position.y)
).toRgb()
}
}
}
}
}) {
for (x in 0..rainbowWidth) {
for (y in 0..height) {
drawRect(
color = currentColor.toHsv().copy(
hue = calcHue(x.toFloat()),
saturation = calcSaturation(y.toFloat())
).toRgb(),
topLeft = Offset(x.toFloat(), y.toFloat()),
size = Size(1f, 1f)
)
}
}
val valueBandX = rainbowWidth + 1
for (y in 0..height) {
drawRect(
color = currentColor.toHsv().copy(value = calcValue(y.toFloat())).toRgb(),
topLeft = Offset(valueBandX.toFloat(), y.toFloat()),
size = Size(bandWidth, 1f)
)
}
val circleX = (currentColor.toHsv().hue / 360) * rainbowWidth
val circleY = (1 - currentColor.toHsv().saturation) * height
drawCircle(
center = Offset(circleX, circleY),
color = Color.Black,
radius = 5f,
style = Stroke(width = 3f)
)
}
}
}
}
fun Color.toHexString() = "0x" + toArgb().toUInt().toString(16)
fun limit(value: Float, min: Float, max: Float) = minOf(
maxOf(value, min),
max
)
fun limit0to1(value: Float) = limit(value = value, 0f, 1f)

75
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/color/HSV.kt

@ -1,75 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.color
import androidx.compose.ui.graphics.Color
import kotlin.math.abs
data class HSV(
/**
* 0.0 .. 360.0
*/
val hue: Float,
/**
* 0.0 .. 1.0
*/
val saturation: Float,
/**
* 0.0 . 1.0¬
*/
val value: Float
) {
companion object {
const val HUE_MAX_VALUE = 360f
}
}
/**
* Convert to HSV color space
* https://www.rapidtables.com/convert/color/rgb-to-hsv.html
*/
fun Color.toHsv(): HSV {
val max = maxOf(red, green, blue)
val min = minOf(red, green, blue)
val delta = max - min
val h = when {
delta == 0f -> 0f
max == red -> 60 * ((green - blue) / delta).mod(6f)
max == green -> 60 * ((blue - red) / delta + 2)
max == blue -> 60 * ((red - green) / delta + 4)
else -> 0f
}
val s = when {
max == 0f -> 0f
else -> delta / max
}
val v = max
return HSV(
hue = h,
saturation = s,
value = v
)
}
/**
* Convert to RGB color space
* https://www.rapidtables.com/convert/color/hsv-to-rgb.html
*/
fun HSV.toRgb(): Color {
val c = value * saturation
val x = minOf(c * (1 - abs((hue / 60).mod(2f) - 1)), 1f)
val m = value - c
val tempColor = when {
hue >= 0 && hue < 60 -> Color(c, x, 0f)
hue >= 60 && hue < 120 -> Color(x, c, 0f)
hue >= 120 && hue < 180 -> Color(0f, c, x)
hue >= 180 && hue < 240 -> Color(0f, x, c)
hue >= 240 && hue < 300 -> Color(x, 0f, c)
else -> Color(c, 0f, x)
}
return Color(minOf(m + tempColor.red, 1f), minOf(m + tempColor.green, 1f), minOf(m + tempColor.blue, 1f))
}

44
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/panel/ComposeToolWindow.kt

@ -1,44 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.panel
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.awt.ComposePanel
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindow
import com.intellij.openapi.wm.ToolWindowFactory
import com.intellij.ui.content.ContentFactory
import com.jetbrains.compose.IntellijTheme
import java.awt.Dimension
class ComposeToolWindow : ToolWindowFactory, DumbAware {
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
ApplicationManager.getApplication().invokeLater {
toolWindow.contentManager.addContent(
ContentFactory.SERVICE.getInstance().createContent(
ComposePanel().apply {
size = Dimension(300, 300)
setContent {
IntellijTheme(project) {
CounterPanel(stateWithIdeLifecycle)
}
}
},
"Compose tool window",
false
)
)
}
}
companion object {
val stateWithIdeLifecycle = mutableStateOf(CounterState())
}
}

36
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/panel/CounterPanel.kt

@ -1,36 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.panel
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.*
@Composable
fun CounterPanel(stateWithIdeLifecycle: MutableState<CounterState>) {
var stateInline by remember { mutableStateOf(CounterState()) }
Column {
Text("Counter with IDE lifecycle: ${stateWithIdeLifecycle.value.counter}")
Button(onClick = {
stateWithIdeLifecycle.value = stateWithIdeLifecycle.value.copy(
counter = stateWithIdeLifecycle.value.counter + 1
)
}) {
Text("Increment state with IDE lifecycle")
}
Text("Counter with @Composable lifecycle: ${stateInline.counter}")
Button(onClick = {
stateInline = stateInline.copy(
counter = stateInline.counter + 1
)
}) {
Text("Increment state with @Composable lifecycle")
}
}
}

10
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/panel/CounterState.kt

@ -1,10 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.panel
data class CounterState(
val counter: Int = 0
)

9
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Color.kt

@ -1,9 +0,0 @@
package com.jetbrains.compose.theme
import androidx.compose.ui.graphics.Color
val green200 = Color(0xffa5d6a7)
val green500 = Color(0xff4caf50)
val green700 = Color(0xff388e3c)
val teal200 = Color(0xff80deea)

11
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Shape.kt

@ -1,11 +0,0 @@
package com.jetbrains.compose.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)

46
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Theme.kt

@ -1,46 +0,0 @@
package com.jetbrains.compose.theme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import com.jetbrains.compose.theme.intellij.SwingColor
private val DarkGreenColorPalette = darkColors(
primary = green200,
primaryVariant = green700,
secondary = teal200,
onPrimary = Color.Black,
onSecondary = Color.White,
error = Color.Red,
)
private val LightGreenColorPalette = lightColors(
primary = green500,
primaryVariant = green700,
secondary = teal200,
onPrimary = Color.White,
onSurface = Color.Black
)
@Composable
fun WidgetTheme(
darkTheme: Boolean = false,
content: @Composable() () -> Unit,
) {
val colors = if (darkTheme) DarkGreenColorPalette else LightGreenColorPalette
val swingColor = SwingColor()
MaterialTheme(
colors = colors.copy(
background = swingColor.background,
onBackground = swingColor.onBackground,
surface = swingColor.background,
onSurface = swingColor.onBackground,
),
typography = typography,
shapes = shapes,
content = content
)
}

43
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Type.kt

@ -1,43 +0,0 @@
package com.jetbrains.compose.theme
import androidx.compose.material.Typography
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
val typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
body2 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp
),
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
),
subtitle1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
color = Color.Gray
),
subtitle2 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
color = Color.Gray
),
)

61
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/SwingColor.kt

@ -1,61 +0,0 @@
package com.jetbrains.compose.theme.intellij
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import com.intellij.ide.ui.LafManagerListener
import com.intellij.openapi.application.ApplicationManager
import javax.swing.UIManager
import java.awt.Color as AWTColor
interface SwingColor {
val background: Color
val onBackground: Color
}
@Composable
fun SwingColor(): SwingColor {
val swingColor = remember { SwingColorImpl() }
val messageBus = remember {
ApplicationManager.getApplication().messageBus.connect()
}
remember(messageBus) {
messageBus.subscribe(
LafManagerListener.TOPIC,
ThemeChangeListener(swingColor::updateCurrentColors)
)
}
DisposableEffect(messageBus) {
onDispose {
messageBus.disconnect()
}
}
return swingColor
}
private class SwingColorImpl : SwingColor {
private val _backgroundState: MutableState<Color> = mutableStateOf(getBackgroundColor)
private val _onBackgroundState: MutableState<Color> = mutableStateOf(getOnBackgroundColor)
override val background: Color get() = _backgroundState.value
override val onBackground: Color get() = _onBackgroundState.value
private val getBackgroundColor get() = getColor(BACKGROUND_KEY)
private val getOnBackgroundColor get() = getColor(ON_BACKGROUND_KEY)
fun updateCurrentColors() {
_backgroundState.value = getBackgroundColor
_onBackgroundState.value = getOnBackgroundColor
}
private val AWTColor.asComposeColor: Color get() = Color(red, green, blue, alpha)
private fun getColor(key: String): Color = UIManager.getColor(key).asComposeColor
companion object {
private const val BACKGROUND_KEY = "Panel.background"
private const val ON_BACKGROUND_KEY = "Panel.foreground"
}
}

13
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/ThemeChangeListener.kt

@ -1,13 +0,0 @@
package com.jetbrains.compose.theme.intellij
import com.intellij.ide.ui.LafManager
import com.intellij.ide.ui.LafManagerListener
internal class ThemeChangeListener(
val updateColors: () -> Unit
) : LafManagerListener {
override fun lookAndFeelChanged(source: LafManager) {
updateColors()
}
}

57
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Buttons.kt

@ -1,57 +0,0 @@
package com.jetbrains.compose.widgets
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.TextButton
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun Buttons() {
Row {
val btnEnabled = remember { mutableStateOf(true) }
Button(
onClick = { btnEnabled.value = !btnEnabled.value},
modifier = Modifier.padding(8.dp),
enabled = btnEnabled.value
) {
Icon(
imageVector = Icons.Default.FavoriteBorder,
contentDescription = "FavoriteBorder",
modifier = Modifier.padding(end = 4.dp)
)
Text(text = "Button")
}
val btnTextEnabled = remember { mutableStateOf(true) }
TextButton(
onClick = { btnTextEnabled.value = !btnTextEnabled.value },
modifier = Modifier.padding(8.dp),
enabled = btnTextEnabled.value
) {
Text(text = "Text Button")
}
OutlinedButton(
onClick = {
btnEnabled.value = true
btnTextEnabled.value = true
},
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Refresh",
modifier = Modifier.padding(0.dp)
)
}
}
}

71
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/LazyScrollable.kt

@ -1,71 +0,0 @@
package com.jetbrains.compose.widgets
import androidx.compose.desktop.DesktopTheme
import androidx.compose.foundation.background
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.VerticalScrollbar
import androidx.compose.material.Text
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun LazyScrollable() {
MaterialTheme {
DesktopTheme {
Box(
modifier = Modifier.fillMaxSize()
.padding(10.dp)
) {
val state = rememberLazyListState()
val itemCount = 100
LazyColumn(Modifier.fillMaxSize().padding(end = 12.dp), state) {
items(itemCount) { x ->
TextBox("Item in ScrollableColumn #$x")
Spacer(modifier = Modifier.height(5.dp))
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
adapter = rememberScrollbarAdapter(
scrollState = state
)
)
}
}
}
}
@Composable
private fun TextBox(text: String = "Item") {
Surface(
color = Color(135, 135, 135, 40),
shape = RoundedCornerShape(4.dp)
) {
Box(
modifier = Modifier.height(32.dp)
.fillMaxWidth()
.padding(start = 10.dp),
contentAlignment = Alignment.CenterStart
) {
Text(text = text)
}
}
}

39
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Loaders.kt

@ -1,39 +0,0 @@
package com.jetbrains.compose.widgets
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun Loaders() {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp)
) {
Box(
modifier = Modifier.height(30.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp, 20.dp),
strokeWidth = 4.dp
)
}
Box(
modifier = Modifier
.height(30.dp)
.padding(start = 8.dp),
contentAlignment = Alignment.Center
) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
}
}
}

53
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/TextInputs.kt

@ -1,53 +0,0 @@
package com.jetbrains.compose.widgets
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.OutlinedTextField
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
@Composable
fun TextInputs() {
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp)
) {
var name by remember { mutableStateOf(TextFieldValue("")) }
var password by remember { mutableStateOf(TextFieldValue("")) }
TextField(
value = name,
onValueChange = { newValue -> name = newValue },
modifier = Modifier.padding(8.dp).fillMaxWidth(),
textStyle = TextStyle(fontFamily = FontFamily.SansSerif),
label = { Text("Account:") },
placeholder = { Text("account name") }
)
OutlinedTextField(
value = password,
modifier = Modifier.padding(8.dp).fillMaxWidth(),
label = { Text(text = "Password:") },
placeholder = { Text(text = "your password") },
textStyle = TextStyle(fontFamily = FontFamily.SansSerif),
visualTransformation = PasswordVisualTransformation(),
onValueChange = {
password = it
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
}
}

90
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Toggles.kt

@ -1,90 +0,0 @@
package com.jetbrains.compose.widgets
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Checkbox
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.Slider
import androidx.compose.material.Switch
import androidx.compose.material.SwitchDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun Toggles() {
Column {
Row {
Column(
modifier = Modifier.padding(16.dp)
) {
var checked by remember { mutableStateOf(true) }
Checkbox(
checked = checked,
modifier = Modifier.padding(8.dp),
onCheckedChange = { checked = !checked }
)
var switched by remember { mutableStateOf(true) }
Switch(
checked = switched,
colors = SwitchDefaults.colors(checkedThumbColor = MaterialTheme.colors.primary),
modifier = Modifier.padding(8.dp),
onCheckedChange = { switched = it }
)
}
Column(
modifier = Modifier.padding(16.dp)
) {
var selected by remember { mutableStateOf("Kotlin") }
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(selected = selected == "Kotlin", onClick = { selected = "Kotlin" })
Text(
text = "Kotlin",
modifier = Modifier.clickable(onClick = { selected = "Kotlin" }).padding(start = 4.dp)
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(selected = selected == "Java", onClick = { selected = "Java" })
Text(
text = "Java",
modifier = Modifier.clickable(onClick = { selected = "Java" }).padding(start = 4.dp)
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(selected = selected == "Swift", onClick = { selected = "Swift" })
Text(
text = "Swift",
modifier = Modifier.clickable(onClick = { selected = "Swift" }).padding(start = 4.dp)
)
}
}
}
var sliderState by remember { mutableStateOf(0f) }
Slider(value = sliderState, modifier = Modifier.fillMaxWidth().padding(8.dp),
onValueChange = { newValue ->
sliderState = newValue
}
)
var sliderState2 by remember { mutableStateOf(20f) }
Slider(value = sliderState2, modifier = Modifier.fillMaxWidth().padding(8.dp),
valueRange = 0f..100f,
steps = 5,
onValueChange = { newValue ->
sliderState2 = newValue
}
)
}
}

29
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/META-INF/plugin.xml

@ -1,29 +0,0 @@
<idea-plugin>
<id>com.jetbrains.ComposeDemoPlugin</id>
<name>Jetpack Compose for Desktop Demo</name>
<vendor email="support@jetbrains.com" url="http://www.jetbrains.com">JetBrains</vendor>
<description><![CDATA[
A plugin demonstrates Jetpack compose capabilities on IntelliJ Platform
]]></description>
<!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<depends>com.intellij.modules.platform</depends>
<depends>org.jetbrains.compose.intellij.platform</depends>
<depends>org.jetbrains.kotlin</depends>
<actions>
<!-- Add your actions here -->
<action id="com.jetbrains.compose.ComposeDemoAction" class="com.jetbrains.compose.ComposeDemoAction"
text="Show Compose Demo...">
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
</actions>
<extensions defaultExtensionNs="com.intellij">
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="com.jetbrains.compose.color.ColorLineMarkerProvider" />
<toolWindow id="Compose" anchor="right" secondary="false" factoryClass="com.jetbrains.compose.panel.ComposeToolWindow"
icon="/icons/compose.svg"/>
</extensions>
</idea-plugin>

1
experimental/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/icons/compose.svg

@ -1 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path d="M8.5 1L14 4l-3.317 1.81L8.5 4.5 6.317 5.81 3 4l5.5-3z" fill="#3DDC84"/><path d="M3 5l3 1.589V9l2 1.2V14l-5-3V5z" fill="#073042"/><path d="M14 5v6l-5 3v-3.8L11 9V6.589L14 5z" fill="#4285F4"/><path fill="#D6F0FF" d="M8.5 4.5L11 6v3l-2.5 1.5L6 9V6z"/></g></svg>

Before

Width:  |  Height:  |  Size: 438 B

25
experimental/examples/intellij-plugin-with-experimental-shared-base/src/test/kotlin/com/jetbrains/compose/color/ColorPickerUITest.kt

@ -1,25 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.color
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
fun main() = application {
val windowState = remember { WindowState(width = 400.dp, height = 400.dp) }
Window(
onCloseRequest = ::exitApplication,
title = "ColorPicker",
state = windowState
) {
ColorPicker(mutableStateOf(Color(0xffaabbcc)))
}
}

22
experimental/examples/intellij-plugin-with-experimental-shared-base/src/test/kotlin/com/jetbrains/compose/color/HSVTest.kt

@ -1,22 +0,0 @@
/*
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package com.jetbrains.compose.color
import androidx.compose.ui.graphics.Color
import org.junit.Test
import kotlin.test.assertEquals
class HSVTest {
@Test
fun testGreenToHsv() {
val greenRgb = Color(0xff00ff00)
val result = greenRgb.toHsv()
assertEquals(HSV(120f, 1f, 1f), result)
assertEquals(greenRgb, result.toRgb())
}
}

8
gradle-plugins/compose/build.gradle.kts

@ -43,12 +43,6 @@ val embeddedDependencies by configurations.creating {
isTransitive = false isTransitive = false
} }
val kgpResourcesDevVersion = "2.0.0-dev-17632"
//KMP resources API available since ^kgpResourcesDevVersion
repositories {
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/")
}
dependencies { dependencies {
// By default, Gradle resolves plugins only via Gradle Plugin Portal. // By default, Gradle resolves plugins only via Gradle Plugin Portal.
// To avoid declaring an additional repo, all dependencies must: // To avoid declaring an additional repo, all dependencies must:
@ -63,7 +57,7 @@ dependencies {
compileOnly(gradleApi()) compileOnly(gradleApi())
compileOnly(localGroovy()) compileOnly(localGroovy())
compileOnly(kotlin("gradle-plugin", kgpResourcesDevVersion)) compileOnly(kotlin("gradle-plugin"))
compileOnly(kotlin("native-utils")) compileOnly(kotlin("native-utils"))
compileOnly(libs.plugin.android) compileOnly(libs.plugin.android)
compileOnly(libs.plugin.android.api) compileOnly(libs.plugin.android.api)

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

@ -24,7 +24,7 @@ internal object ComposeCompilerCompatibility {
"1.9.21" to "1.5.4", "1.9.21" to "1.5.4",
"1.9.22" to "1.5.8.1", "1.9.22" to "1.5.8.1",
"1.9.23" to "1.5.13.5", "1.9.23" to "1.5.13.5",
"1.9.24" to "1.5.14", "1.9.24" to "1.5.14.1-beta02",
"2.0.0-Beta1" to "1.5.4-dev1-kt2.0.0-Beta1", "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", "2.0.0-Beta4" to "1.5.9-kt-2.0.0-Beta4",
"2.0.0-Beta5" to "1.5.11-kt-2.0.0-Beta5", "2.0.0-Beta5" to "1.5.11-kt-2.0.0-Beta5",

55
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt

@ -8,12 +8,12 @@ package org.jetbrains.compose
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.jetbrains.compose.internal.ComposeCompilerArtifactProvider import org.jetbrains.compose.internal.ComposeCompilerArtifactProvider
import org.jetbrains.compose.internal.KOTLIN_ANDROID_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_JS_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.internal.Version import org.jetbrains.compose.internal.Version
import org.jetbrains.compose.internal.ideaIsInSyncProvider import org.jetbrains.compose.internal.ideaIsInSyncProvider
import org.jetbrains.compose.internal.mppExtOrNull
import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService
import org.jetbrains.compose.internal.webExt import org.jetbrains.compose.internal.webExt
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
@ -24,14 +24,18 @@ import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
internal fun Project.configureComposeCompilerPlugin() { internal fun Project.configureComposeCompilerPlugin() {
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { plugin -> //only one of them can be applied to the project
configureComposeCompilerPlugin(plugin as KotlinBasePlugin) listOf(
} KOTLIN_MPP_PLUGIN_ID,
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { plugin -> KOTLIN_JVM_PLUGIN_ID,
configureComposeCompilerPlugin(plugin as KotlinBasePlugin) KOTLIN_ANDROID_PLUGIN_ID,
KOTLIN_JS_PLUGIN_ID
).forEach { pluginId ->
plugins.withId(pluginId) { plugin ->
configureComposeCompilerPlugin(plugin as KotlinBasePlugin)
}
} }
} }
@ -77,33 +81,9 @@ class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
} }
applicableForPlatformTypes = composeExt.platformTypes applicableForPlatformTypes = composeExt.platformTypes
collectUnsupportedCompilerPluginUsages(target)
} }
} }
private fun collectUnsupportedCompilerPluginUsages(project: Project) {
fun Project.hasNonJvmTargets(): Boolean {
val nonJvmTargets = setOf(KotlinPlatformType.native, KotlinPlatformType.js, KotlinPlatformType.wasm)
return mppExtOrNull?.targets?.any {
it.platformType in nonJvmTargets
} ?: false
}
fun SubpluginArtifact.isNonJBComposeCompiler(): Boolean {
return !groupId.startsWith("org.jetbrains.compose.compiler")
}
ConfigurationProblemReporterService.registerUnsupportedPluginProvider(
project,
project.provider {
composeCompilerArtifactProvider.compilerArtifact.takeIf {
project.hasNonJvmTargets() && it.isNonJBComposeCompiler()
}
}
)
}
override fun getCompilerPluginId(): String = override fun getCompilerPluginId(): String =
"androidx.compose.compiler.plugins.kotlin" "androidx.compose.compiler.plugins.kotlin"
@ -146,14 +126,3 @@ class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
private fun options(vararg options: Pair<String, String>): List<SubpluginOption> = private fun options(vararg options: Pair<String, String>): List<SubpluginOption> =
options.map { SubpluginOption(it.first, it.second) } options.map { SubpluginOption(it.first, it.second) }
} }
private const val COMPOSE_COMPILER_COMPATIBILITY_LINK =
"https://github.com/JetBrains/compose-jb/blob/master/VERSIONING.md#using-compose-multiplatform-compiler"
internal fun createWarningAboutNonCompatibleCompiler(currentCompilerPluginGroupId: String): String {
return """
WARNING: Usage of the Custom Compose Compiler plugin ('$currentCompilerPluginGroupId')
with non-JVM targets (Kotlin/Native, Kotlin/JS, Kotlin/WASM) is not supported.
For more information, please visit: $COMPOSE_COMPILER_COMPATIBILITY_LINK
""".trimMargin()
}

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

Loading…
Cancel
Save