Skip to content

Commit

Permalink
Update Compose to 1.7.0-beta02 (#595)
Browse files Browse the repository at this point in the history
This version of Compose deprecates the resource loading APIs we've been
using, and brutally removes ResourceLoader, so we need to migrate to the
new (experimental) CMP Resources API.

Things seem to work fine after upgrading and migrating. Would appreciate
further testing on platforms other than macOS before merging.
  • Loading branch information
rock3r authored Sep 17, 2024
1 parent 5d4623d commit b367950
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 40 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
commonmark = "0.22.0"
composeDesktop = "1.7.0-beta01"
composeDesktop = "1.7.0-beta02"
detekt = "1.23.6"
dokka = "1.9.20"
idea = "2024.2.1"
Expand Down
1 change: 1 addition & 0 deletions samples/standalone/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation(projects.markdown.extension.gfmAlerts)
implementation(projects.markdown.extension.autolink)
implementation(compose.desktop.currentOs) { exclude(group = "org.jetbrains.compose.material") }
implementation(compose.components.resources)
implementation(libs.intellijPlatform.icons)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isAltPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.ResourceLoader
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.Density
import androidx.compose.ui.window.application
import java.io.InputStream
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.decodeToSvgPainter
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.foundation.util.JewelLogger
import org.jetbrains.jewel.intui.standalone.Inter
Expand Down Expand Up @@ -113,5 +112,13 @@ private fun processKeyShortcuts(keyEvent: KeyEvent, onNavigateTo: (String) -> Un
}
}

private fun svgResource(resourcePath: String, loader: ResourceLoader = ResourceLoader.Default): Painter =
loader.load(resourcePath).use { stream: InputStream -> loadSvgPainter(stream, Density(1f)) }
@Suppress("SameParameterValue")
@OptIn(ExperimentalResourceApi::class)
private fun svgResource(resourcePath: String): Painter =
checkNotNull(ResourceLoader.javaClass.classLoader.getResourceAsStream(resourcePath)) {
"Could not load resource $resourcePath: it does not exist or can't be read."
}
.readAllBytes()
.decodeToSvgPainter(Density(1f))

private object ResourceLoader
2 changes: 1 addition & 1 deletion ui/api/ui.api
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ public final class org/jetbrains/jewel/ui/component/IconKt {
public static final fun Icon-ww6aTOc (Landroidx/compose/ui/graphics/ImageBitmap;Ljava/lang/String;Landroidx/compose/ui/Modifier;JLandroidx/compose/runtime/Composer;II)V
public static final fun Icon-ww6aTOc (Landroidx/compose/ui/graphics/painter/Painter;Ljava/lang/String;Landroidx/compose/ui/Modifier;JLandroidx/compose/runtime/Composer;II)V
public static final fun Icon-ww6aTOc (Landroidx/compose/ui/graphics/vector/ImageVector;Ljava/lang/String;Landroidx/compose/ui/Modifier;JLandroidx/compose/runtime/Composer;II)V
public static final fun painterResource (Ljava/lang/String;Landroidx/compose/ui/res/ResourceLoader;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/painter/Painter;
public static final fun painterResource (Ljava/lang/String;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/graphics/painter/Painter;
}

public final class org/jetbrains/jewel/ui/component/InputFieldState : org/jetbrains/jewel/foundation/state/FocusableComponentState {
Expand Down
1 change: 1 addition & 0 deletions ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ private val composeVersion

dependencies {
api(projects.foundation)
implementation(compose.components.resources)
iconGeneration(libs.intellijPlatform.util.ui)
iconGeneration(libs.intellijPlatform.icons)
testImplementation(compose.desktop.uiTestJUnit4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.decodeToSvgPainter
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.ui.component.styling.CircularProgressStyle
import org.jetbrains.jewel.ui.theme.circularProgressStyle
Expand Down Expand Up @@ -57,6 +58,7 @@ public fun CircularProgressIndicatorBig(
)
}

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun CircularProgressIndicatorImpl(
modifier: Modifier = Modifier,
Expand All @@ -73,7 +75,7 @@ private fun CircularProgressIndicatorImpl(
value =
withContext(dispatcher) {
frameRetriever(style.color.takeOrElse { defaultColor }).map {
loadSvgPainter(it.byteInputStream(), density)
it.toByteArray().decodeToSvgPainter(density)
}
}
}
Expand Down
49 changes: 25 additions & 24 deletions ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Icon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,22 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.ResourceLoader
import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.loadXmlImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import java.io.InputStream
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.decodeToImageBitmap
import org.jetbrains.compose.resources.decodeToImageVector
import org.jetbrains.compose.resources.decodeToSvgPainter
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.ui.icon.IconKey
import org.jetbrains.jewel.ui.icon.newUiChecker
import org.jetbrains.jewel.ui.painter.PainterHint
import org.jetbrains.jewel.ui.painter.rememberResourcePainterProvider
import org.jetbrains.jewel.ui.util.thenIf
import org.xml.sax.InputSource

@Deprecated(
"Use the IconKey-based API instead",
Expand Down Expand Up @@ -310,37 +308,40 @@ public fun Icon(
}

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

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun rememberSvgResource(resourcePath: String, loader: ResourceLoader = ResourceLoader.Default): Painter {
private fun rememberSvgResource(path: String): Painter {
val density = LocalDensity.current
return remember(resourcePath, density, loader) { useResource(resourcePath, loader) { loadSvgPainter(it, density) } }
return remember(density, path) { readResourceBytes(path).decodeToSvgPainter(density) }
}

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun rememberVectorXmlResource(resourcePath: String, loader: ResourceLoader = ResourceLoader.Default): Painter {
private fun rememberVectorXmlResource(path: String): Painter {
val density = LocalDensity.current
val image =
remember(resourcePath, density, loader) {
useResource(resourcePath, loader) { loadXmlImageVector(InputSource(it), density) }
}
return rememberVectorPainter(image)
val imageVector = remember(density, path) { readResourceBytes(path).decodeToImageVector(density) }
return rememberVectorPainter(imageVector)
}

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun rememberBitmapResource(resourcePath: String, loader: ResourceLoader = ResourceLoader.Default): Painter {
val image = remember(resourcePath) { useResource(resourcePath, loader, ::loadImageBitmap) }
return BitmapPainter(image)
}
private fun rememberBitmapResource(path: String): Painter =
remember(path) { BitmapPainter(readResourceBytes(path).decodeToImageBitmap()) }

private inline fun <T> useResource(resourcePath: String, loader: ResourceLoader, block: (InputStream) -> T): T =
loader.load(resourcePath).use(block)
private object ResourceLoader

private fun readResourceBytes(resourcePath: String) =
checkNotNull(ResourceLoader.javaClass.classLoader.getResourceAsStream(resourcePath)) {
"Could not load resource $resourcePath: it does not exist or can't be read."
}
.readAllBytes()

private fun Modifier.defaultSizeFor(painter: Painter) =
thenIf(painter.intrinsicSize == Size.Unspecified || painter.intrinsicSize.isInfinite()) { DefaultIconSizeModifier }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.loadXmlImageVector
import androidx.compose.ui.unit.Density
import java.io.IOException
import java.io.InputStream
Expand All @@ -27,11 +24,14 @@ import javax.xml.transform.TransformerException
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.decodeToImageBitmap
import org.jetbrains.compose.resources.decodeToImageVector
import org.jetbrains.compose.resources.decodeToSvgPainter
import org.jetbrains.jewel.foundation.util.myLogger
import org.jetbrains.jewel.ui.icon.IconKey
import org.jetbrains.jewel.ui.icon.LocalNewUiChecker
import org.w3c.dom.Document
import org.xml.sax.InputSource

private val errorPainter = ColorPainter(Color.Magenta)

Expand Down Expand Up @@ -137,14 +137,15 @@ public class ResourcePainterProvider(private val basePath: String, vararg classL
return null
}

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun createSvgPainter(scope: Scope, url: URL): Painter =
tryLoadingResource(
url = url,
loadingAction = { resourceUrl ->
patchSvg(scope, url.openStream(), scope.acceptedHints).use { inputStream ->
logger.debug("Loading icon $basePath(${scope.acceptedHints.joinToString()}) from $resourceUrl")
loadSvgPainter(inputStream, scope)
inputStream.readAllBytes().decodeToSvgPainter(scope)
}
},
paintAction = { it },
Expand All @@ -171,22 +172,24 @@ public class ResourcePainterProvider(private val basePath: String, vararg classL
}
}

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun createVectorDrawablePainter(scope: Scope, url: URL): Painter =
tryLoadingResource(
url = url,
loadingAction = { resourceUrl ->
resourceUrl.openStream().use { loadXmlImageVector(InputSource(it), scope) }
resourceUrl.openStream().use { inputStream -> inputStream.readAllBytes().decodeToImageVector(scope) }
},
paintAction = { rememberVectorPainter(it) },
)

@OptIn(ExperimentalResourceApi::class)
@Composable
private fun createBitmapPainter(url: URL) =
tryLoadingResource(
url = url,
loadingAction = { resourceUrl ->
val bitmap = resourceUrl.openStream().use { loadImageBitmap(it) }
val bitmap = resourceUrl.openStream().use { it.readAllBytes().decodeToImageBitmap() }
BitmapPainter(bitmap)
},
paintAction = { it },
Expand Down

0 comments on commit b367950

Please sign in to comment.