Compose Multiplatform Desktop JVM support library for building cross-platform desktop applications with declarative UI framework based on Jetpack Compose
—
Desktop components provide enhanced UI functionality specifically optimized for desktop platforms, including resizable panels, animated image support, and desktop-specific interactions.
The SplitPane component allows creating resizable panels with a draggable splitter.
@ExperimentalSplitPaneApi
@Composable
fun HorizontalSplitPane(
modifier: Modifier = Modifier,
splitPaneState: SplitPaneState = rememberSplitPaneState(),
content: SplitPaneScope.() -> Unit
)
@ExperimentalSplitPaneApi
@Composable
fun VerticalSplitPane(
modifier: Modifier = Modifier,
splitPaneState: SplitPaneState = rememberSplitPaneState(),
content: SplitPaneScope.() -> Unit
)@Stable
class SplitPaneState {
var positionPercentage: Float
fun dispatchRawMovement(delta: Float): Float
}
@ExperimentalSplitPaneApi
@Composable
fun rememberSplitPaneState(
initialPositionPercentage: Float = 0f,
moveEnabled: Boolean = true
): SplitPaneState@ExperimentalSplitPaneApi
interface SplitPaneScope {
fun first(
minSize: Dp = 0.dp,
content: @Composable () -> Unit
)
fun second(
minSize: Dp = 0.dp,
content: @Composable () -> Unit
)
fun splitter(block: SplitterScope.() -> Unit)
}
@ExperimentalSplitPaneApi
interface SplitterScope {
fun visiblePart(content: @Composable () -> Unit)
fun handle(
alignment: SplitterHandleAlignment = SplitterHandleAlignment.ABOVE,
content: @Composable HandleScope.() -> Unit
)
}
@ExperimentalSplitPaneApi
interface HandleScope {
fun Modifier.markAsHandle(): Modifier
}import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.VerticalSplitPane
import org.jetbrains.compose.splitpane.rememberSplitPaneState
@OptIn(ExperimentalSplitPaneApi::class)
@Composable
fun SplitPaneExample() {
val horizontalSplitState = rememberSplitPaneState(initialPositionPercentage = 0.3f)
val verticalSplitState = rememberSplitPaneState(initialPositionPercentage = 0.7f)
HorizontalSplitPane(
splitPaneState = horizontalSplitState
) {
first {
// Left panel content
Box(
modifier = Modifier.fillMaxSize().background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text("Left Panel")
}
}
second {
VerticalSplitPane(
splitPaneState = verticalSplitState
) {
first {
// Top-right panel
Box(
modifier = Modifier.fillMaxSize().background(Color.Blue.copy(alpha = 0.3f)),
contentAlignment = Alignment.Center
) {
Text("Top Right Panel")
}
}
second {
// Bottom-right panel
Box(
modifier = Modifier.fillMaxSize().background(Color.Green.copy(alpha = 0.3f)),
contentAlignment = Alignment.Center
) {
Text("Bottom Right Panel")
}
}
}
}
}
}The AnimatedImage component provides support for displaying animated images like GIFs.
@Composable
fun AnimatedImage(
animatedImageLoader: AnimatedImageLoader,
modifier: Modifier = Modifier,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.Fit,
alignment: Alignment = Alignment.Center,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
)interface AnimatedImageLoader {
suspend fun load(): AnimatedImageLoadResult
}
sealed class AnimatedImageLoadResult {
object Loading : AnimatedImageLoadResult()
class Success(val image: AnimatedImage) : AnimatedImageLoadResult()
class Error(val exception: Throwable) : AnimatedImageLoadResult()
}
abstract class AnimatedImage {
abstract val frames: List<ImageBitmap>
abstract val frameDelays: List<Int>
abstract fun intrinsicSize(): IntSize
}class ResourceAnimatedImageLoader(
resourcePath: String
) : AnimatedImageLoader
class NetworkAnimatedImageLoader(
url: String,
httpClient: HttpClient? = null
) : AnimatedImageLoader
@Composable
fun LocalAnimatedImageLoader.current: AnimatedImageLoaderimport org.jetbrains.compose.animatedimage.AnimatedImage
import org.jetbrains.compose.animatedimage.ResourceAnimatedImageLoader
import org.jetbrains.compose.animatedimage.NetworkAnimatedImageLoader
import org.jetbrains.compose.animatedimage.LocalAnimatedImageLoader
@Composable
fun AnimatedImageExample() {
Column {
// Load from resources
AnimatedImage(
animatedImageLoader = ResourceAnimatedImageLoader("animations/loading.gif"),
modifier = Modifier.size(100.dp),
contentDescription = "Loading animation"
)
// Load from network
AnimatedImage(
animatedImageLoader = NetworkAnimatedImageLoader("https://example.com/animation.gif"),
modifier = Modifier.size(200.dp),
contentDescription = "Network animation",
contentScale = ContentScale.Crop
)
}
}
// Providing a default loader
@Composable
fun MyApp() {
CompositionLocalProvider(
LocalAnimatedImageLoader provides ResourceAnimatedImageLoader("default.gif")
) {
AnimatedImageExample()
}
}Desktop-specific resource loading utilities.
object ResourceEnvironment {
fun isRunningFromJar(): Boolean
fun getResourcePath(resourcePath: String): String?
}interface ResourceReader {
suspend fun read(path: String): ByteArray
suspend fun readText(path: String): String
}
object DesktopResourceReader : ResourceReaderimport org.jetbrains.compose.resources.ResourceReader
import org.jetbrains.compose.resources.ResourceEnvironment
@Composable
fun ResourceExample() {
var imageData by remember { mutableStateOf<ByteArray?>(null) }
LaunchedEffect(Unit) {
try {
imageData = DesktopResourceReader.read("images/icon.png")
} catch (e: Exception) {
println("Failed to load resource: ${e.message}")
}
}
imageData?.let { data ->
// Use the image data
Image(
bitmap = org.jetbrains.skia.Image.makeFromEncoded(data).toComposeImageBitmap(),
contentDescription = "App icon"
)
}
}Add desktop components to your project:
dependencies {
// SplitPane component (experimental)
implementation(compose.desktop.components.splitPane)
// AnimatedImage component (experimental)
implementation(compose.desktop.components.animatedImage)
// Resources support
implementation(compose.components.resources)
}The Browser component provides embedded web browser functionality using CEF (Chromium Embedded Framework).
interface Browser {
fun load(url: String)
}
class BrowserView : Browser {
override fun load(url: String)
fun view(): ComposeWindow
fun dismiss()
}import org.jetbrains.compose.desktop.browser.BrowserView
@Composable
fun BrowserExample() {
val browser = remember { BrowserView() }
Column {
Button(
onClick = { browser.load("https://example.com") }
) {
Text("Load Website")
}
Button(
onClick = { browser.dismiss() }
) {
Text("Close Browser")
}
}
}The VideoPlayer component provides video playback functionality using VLC backend.
@Composable
fun VideoPlayerImpl(
url: String,
isResumed: Boolean = true,
volume: Float = 1f,
speed: Float = 1f,
seek: Long = 0,
isFullscreen: Boolean = false,
onFinish: () -> Unit = {}
)import org.jetbrains.compose.videoplayer.VideoPlayerImpl
@Composable
fun VideoPlayerExample() {
var isPlaying by remember { mutableStateOf(true) }
var volume by remember { mutableStateOf(1f) }
Column {
VideoPlayerImpl(
url = "file:///path/to/video.mp4",
isResumed = isPlaying,
volume = volume,
speed = 1f,
onFinish = { println("Video finished playing") }
)
Row {
Button(
onClick = { isPlaying = !isPlaying }
) {
Text(if (isPlaying) "Pause" else "Play")
}
Slider(
value = volume,
onValueChange = { volume = it },
valueRange = 0f..1f
)
}
}
}Desktop components are marked with @ExperimentalComposeLibrary annotation, indicating they are experimental and their API may change in future versions.
@OptIn(ExperimentalComposeLibrary::class)
@Composable
fun MyDesktopApp() {
// Use experimental components
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-desktop--desktop-jvm