Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMP-1108: Deprecate resources in compose.ui in favour of the new resource library #1457

Merged
merged 9 commits into from
Sep 4, 2024

Conversation

terrakok
Copy link
Member

@terrakok terrakok commented Jul 18, 2024

To work with resources in the desktop compose the recommended way is to use the special resources library.

Fixes https://youtrack.jetbrains.com/issue/CMP-1108

Migration snippet

To read java resources by path as Painters you may use:

@Composable
internal fun painterResource(
    resourcePath: String
): Painter = when (resourcePath.substringAfterLast(".")) {
    "svg" -> rememberSvgResource(resourcePath)
    "xml" -> rememberVectorXmlResource(resourcePath)
    else -> rememberBitmapResource(resourcePath)
}

@Composable
internal fun rememberBitmapResource(path: String): Painter {
    return remember(path) { BitmapPainter(readResourceBytes(path).decodeToImageBitmap()) }
}

@Composable
internal fun rememberVectorXmlResource(path: String): Painter {
    val density = LocalDensity.current
    val imageVector = remember(density, path) { readResourceBytes(path).decodeToImageVector(density) }
    return rememberVectorPainter(imageVector)
}

@Composable
internal fun rememberSvgResource(path: String): Painter {
    val density = LocalDensity.current
    return remember(density, path) { readResourceBytes(path).decodeToSvgPainter(density) }
}

private object ResourceLoader
private fun readResourceBytes(resourcePath: String) =
    ResourceLoader.javaClass.classLoader.getResourceAsStream(resourcePath).readAllBytes()

Release Notes

Breaking changes - Resources

  • Deprecate resources in compose.ui in favour of the new resource library

@terrakok terrakok requested a review from igordmn July 18, 2024 14:08
@igordmn igordmn requested a review from m-sasha July 18, 2024 15:40
@ExperimentalComposeUiApi
@Deprecated("Migrate to the compose resources library")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to

  1. Remove experimental
  2. Deprecate experimental (just remove)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot delete these function because they are being used in non experimental public API 🤷
Example:

//non experimental
fun painterResource(resourcePath: String, loader: ResourceLoader): Painter

//non experimental
private fun rememberSvgResource(
    resourcePath: String, 
    loader: ResourceLoader = ResourceLoader.Default
): Painter

//experimental
internal inline fun <T> useResource(
    resourcePath: String,
    loader: ResourceLoader,
    block: (InputStream) -> T
): T

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make it internal then?

@terrakok
Copy link
Member Author

terrakok commented Aug 28, 2024

Checked with Compose 1.7.0 and Kotlin 2.0.20.
Found two problems:

  • CMP-6543 java.lang.NoSuchMethodError: 'void java.io.InputStream.skipNBytes(long)' Fixed
  • CMP-6541 Compose resource accessors aren't generated when Kotlin JVM plugin is applied there is easy workaround
image

(button's texts, scroll items and a logo use compose resources)

@terrakok terrakok requested review from MatkovIvan and igordmn and removed request for m-sasha August 30, 2024 09:25
Copy link
Member

@MatkovIvan MatkovIvan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing experimental marks still doesn't LGTM. We can just remove it instead of committing for endless "deprecation" compatibility support

@igordmn
Copy link
Collaborator

igordmn commented Aug 30, 2024

Removing experimental marks still doesn't LGTM. We can just remove it instead of committing for endless "deprecation"

We can do that for the experimental API, but we should move the implementation inside non-experimental API

@@ -50,7 +50,7 @@ import org.xml.sax.InputSource
* @param loader resources loader
* @return [Painter] used for drawing the loaded resource
*/
@OptIn(ExperimentalComposeUiApi::class)
@Deprecated("Migrate to the Compose resources library. See https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-images-resources.html")
@Composable
fun painterResource(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides painterResource, we need to deprecate in another PR:

fun Font(
    resource: String,
    weight: FontWeight = FontWeight.Normal,
    style: FontStyle = FontStyle.Normal
): Font = ResourceFont(resource, weight, style)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@terrakok, we need to do that as soon as possible. painterResource and Font technically of one kind of features, better to deprecated them in one release.

Copy link
Member Author

@terrakok terrakok Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? The resource: String is not a file path here. It is a name in the system

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ExperimentalComposeUiApi
class ClassLoaderResourceLoader : ResourceLoader {
override fun load(resourcePath: String): InputStream {
@Deprecated("Migrate to the Compose resources library. See https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-images-resources.html")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Deprecated("Migrate to the Compose resources library. See https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-images-resources.html")

@igordmn
Copy link
Collaborator

igordmn commented Sep 3, 2024

Let's wait for a second approval

@terrakok terrakok requested a review from MatkovIvan September 3, 2024 15:24
@terrakok terrakok merged commit bd28372 into jb-main Sep 4, 2024
6 checks passed
@terrakok terrakok deleted the k.tskh/CMP-1108 branch September 4, 2024 10:17
@Nohus
Copy link

Nohus commented Sep 22, 2024

@terrakok I believe this broke org.jetbrains.compose.animatedimage.loadResourceAnimatedImage. You can no longer load gifs from composeResources/files/image.gif. You have to instead move them to the normal non-Compose resources/image.gif. That's an unannounced breaking change in org.jetbrains.compose.components:components-animatedimage-desktop:1.7.0-beta02 that I found out about by user reports of crashing.

@terrakok
Copy link
Member Author

I checked it with the code and it works:

@Composable
internal fun App() = AppTheme {
    val (gif, setGif) = remember { mutableStateOf<AnimatedImage?>(null) }
    LaunchedEffect(Unit) {
        setGif(loadAnimatedImage(Res.getUri("files/olaf.gif")))
    }
    if (gif != null) {
        Image(
            modifier = Modifier.size(100.dp),
            bitmap = gif.animate(),
            contentDescription = null
        )
    }
}

If you have a problem then file an issue on the YouTrack, please

@JetBrains JetBrains locked and limited conversation to collaborators Sep 23, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants