package example.imageviewer.utils import androidx.compose.desktop.AppManager import androidx.compose.desktop.AppWindow import androidx.compose.desktop.WindowEvents import androidx.compose.runtime.* import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.window.MenuBar import kotlinx.coroutines.* import kotlinx.coroutines.swing.Swing import java.awt.image.BufferedImage fun Application( content: @Composable ApplicationScope.() -> Unit ) { GlobalScope.launch(Dispatchers.Swing + ImmediateFrameClock()) { AppManager.setEvents(onWindowsEmpty = null) withRunningRecomposer { recomposer -> val latch = CompletableDeferred() val applier = ApplicationApplier { latch.complete(Unit) } val composition = Composition(applier, recomposer) try { val scope = ApplicationScope(recomposer) composition.setContent { scope.content() } latch.join() } finally { composition.dispose() } } } } class ApplicationScope internal constructor(private val recomposer: Recomposer) { @Composable fun ComposableWindow( title: String = "JetpackDesktopWindow", size: IntSize = IntSize(800, 600), location: IntOffset = IntOffset.Zero, centered: Boolean = true, icon: BufferedImage? = null, menuBar: MenuBar? = null, undecorated: Boolean = false, resizable: Boolean = true, events: WindowEvents = WindowEvents(), onDismissRequest: (() -> Unit)? = null, content: @Composable () -> Unit = {} ) { var isOpened by remember { mutableStateOf(true) } if (!isOpened) return ComposeNode( factory = { val window = AppWindow( title = title, size = size, location = location, centered = centered, icon = icon, menuBar = menuBar, undecorated = undecorated, resizable = resizable, events = events, onDismissRequest = { onDismissRequest?.invoke() isOpened = false } ) window.show(recomposer, content) window }, update = { set(title) { setTitle(it) } set(size) { setSize(it.width, it.height) } // set(location) { setLocation(it.x, it.y) } set(icon) { setIcon(it) } // set(menuBar) { if (it != null) setMenuBar(it) else removeMenuBar() } // set(resizable) { setResizable(it) } // set(events) { setEvents(it) } // set(onDismissRequest) { setDismiss(it) } } ) } } private class ImmediateFrameClock : MonotonicFrameClock { override suspend fun withFrameNanos( onFrame: (frameTimeNanos: Long) -> R ) = onFrame(System.nanoTime()) } private class ApplicationApplier( private val onWindowsEmpty: () -> Unit ) : Applier { private val windows = mutableListOf() override var current: AppWindow? = null override fun insertBottomUp(index: Int, instance: AppWindow?) { requireNotNull(instance) check(current == null) { "Windows cannot be nested!" } windows.add(index, instance) } override fun remove(index: Int, count: Int) { repeat(count) { val window = windows.removeAt(index) if (!window.isClosed) { window.close() } } } override fun move(from: Int, to: Int, count: Int) { if (from > to) { var current = to repeat(count) { val node = windows.removeAt(from) windows.add(current, node) current++ } } else { repeat(count) { val node = windows.removeAt(from) windows.add(to - 1, node) } } } override fun clear() { windows.forEach { if (!it.isClosed) it.close() } windows.clear() } override fun onEndChanges() { if (windows.isEmpty()) { onWindowsEmpty() } } override fun down(node: AppWindow?) { requireNotNull(node) check(current == null) { "Windows cannot be nested!" } current = node } override fun up() { check(current != null) { "Windows cannot be nested!" } current = null } override fun insertTopDown(index: Int, instance: AppWindow?) { // ignored. Building tree bottom-up } }