Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
* limitations under the License.
*/

@file:OptIn(ExperimentalComposeUiApi::class)

package androidx.compose.desktop.examples.swingexample

import java.awt.Color as awtColor
import androidx.compose.foundation.ContextMenuDataProvider
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ContextMenuState
Expand Down Expand Up @@ -44,16 +45,14 @@ import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.LocalSaveableStateRegistry
import androidx.compose.runtime.saveable.SaveableStateRegistry
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.awt.SwingPanel
Expand All @@ -68,7 +67,9 @@ import androidx.compose.ui.window.ApplicationScope
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.launchApplication
import androidx.compose.ui.window.rememberWindowState
import androidx.savedstate.SavedState
import java.awt.BorderLayout
import java.awt.Color as awtColor
import java.awt.Component
import java.awt.Dimension
import java.awt.Graphics
Expand Down Expand Up @@ -100,56 +101,26 @@ fun main() = SwingUtilities.invokeLater {
SwingComposeWindow()
}

private typealias SaveableStateData = Map<String, List<Any?>>
private val globalSavedState = mutableMapOf<String, SavedState?>()

private class GlobalSaveableStateRegistry(
val saveableId: String,
) : SaveableStateRegistry by SaveableStateRegistry(
restoredValues = map[saveableId],
canBeSaved = { true }
) {
fun save() { map[saveableId] = performSave() }
companion object {
private val map = mutableMapOf<String, SaveableStateData>()
}
}

fun createGreenComposePanel() = ComposePanel().also {
val saveableStateRegistry = GlobalSaveableStateRegistry("GREEN")
fun createGreenComposePanel(
savedState: SavedState? = null,
) = ComposePanel(savedState = savedState).also {
it.background = awtColor(55, 155, 55)
it.setContent {
JPopupTextMenuProvider(it) {
CompositionLocalProvider(
LocalSaveableStateRegistry provides saveableStateRegistry,
) {
ComposeContent(background = Color(55, 155, 55))
}
}
DisposableEffect(Unit) {
onDispose {
saveableStateRegistry.save()
println("Dispose composition")
}
ComposeContent(background = Color(55, 155, 55))
}
}
}

fun createBlueComposePanel() = ComposePanel().also {
val saveableStateRegistry = GlobalSaveableStateRegistry("BLUE")
fun createBlueComposePanel(
savedState: SavedState? = null,
) = ComposePanel(savedState = savedState).also {
it.background = awtColor(55, 55, 155)
it.setContent {
CustomTextMenuProvider {
CompositionLocalProvider(
LocalSaveableStateRegistry provides saveableStateRegistry,
) {
ComposeContent(background = Color(55, 55, 155))
}
}
DisposableEffect(Unit) {
onDispose {
saveableStateRegistry.save()
println("Dispose composition")
}
ComposeContent(background = Color(55, 55, 155))
}
}
}
Expand All @@ -173,10 +144,11 @@ fun SwingComposeWindow() {
size = IntSize(40, 40),
action = {
if (composePanel1 != null) {
globalSavedState["GREEN"] = composePanel1!!.saveState()
panel.remove(composePanel1)
composePanel1 = null
} else {
composePanel1 = createGreenComposePanel()
composePanel1 = createGreenComposePanel(globalSavedState["GREEN"])
panel.add(composePanel1, 0)
}
panel.revalidate()
Expand All @@ -191,10 +163,11 @@ fun SwingComposeWindow() {
size = IntSize(40, 40),
action = {
if (composePanel2 != null) {
globalSavedState["BLUE"] = composePanel2!!.saveState()
panel.remove(composePanel2)
composePanel2 = null
} else {
composePanel2 = createBlueComposePanel()
composePanel2 = createBlueComposePanel(globalSavedState["BLUE"])
panel.add(composePanel2)
}
panel.revalidate()
Expand Down
2 changes: 0 additions & 2 deletions compose/ui/ui/api/desktop/ui.api
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,6 @@ public final class androidx/compose/ui/awt/AwtWindow_desktopKt {
public final class androidx/compose/ui/awt/ComposeDialog : javax/swing/JDialog {
public static final field $stable I
public fun <init> ()V
public fun <init> (Ljava/awt/Dialog$ModalityType;)V
public synthetic fun <init> (Ljava/awt/Dialog$ModalityType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/awt/GraphicsConfiguration;)V
public synthetic fun <init> (Ljava/awt/GraphicsConfiguration;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/awt/Window;Ljava/awt/Dialog$ModalityType;Ljava/awt/GraphicsConfiguration;)V
Expand Down
2 changes: 2 additions & 0 deletions compose/ui/ui/api/ui.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -3928,6 +3928,7 @@ final val androidx.compose.ui.platform/androidx_compose_ui_platform_DefaultUiApp
final val androidx.compose.ui.platform/androidx_compose_ui_platform_DefaultViewConfiguration$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_DefaultViewConfiguration$stableprop|#static{}androidx_compose_ui_platform_DefaultViewConfiguration$stableprop[0]
final val androidx.compose.ui.platform/androidx_compose_ui_platform_DelegateRootForTestListener$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_DelegateRootForTestListener$stableprop|#static{}androidx_compose_ui_platform_DelegateRootForTestListener$stableprop[0]
final val androidx.compose.ui.platform/androidx_compose_ui_platform_DelegatingSoftwareKeyboardController$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_DelegatingSoftwareKeyboardController$stableprop|#static{}androidx_compose_ui_platform_DelegatingSoftwareKeyboardController$stableprop[0]
final val androidx.compose.ui.platform/androidx_compose_ui_platform_DisposableSaveableStateRegistry$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_DisposableSaveableStateRegistry$stableprop|#static{}androidx_compose_ui_platform_DisposableSaveableStateRegistry$stableprop[0]
final val androidx.compose.ui.platform/androidx_compose_ui_platform_EmptyViewConfiguration$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_EmptyViewConfiguration$stableprop|#static{}androidx_compose_ui_platform_EmptyViewConfiguration$stableprop[0]
final val androidx.compose.ui.platform/androidx_compose_ui_platform_FlushCoroutineDispatcher$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_FlushCoroutineDispatcher$stableprop|#static{}androidx_compose_ui_platform_FlushCoroutineDispatcher$stableprop[0]
final val androidx.compose.ui.platform/androidx_compose_ui_platform_GlobalSnapshotManager$stableprop // androidx.compose.ui.platform/androidx_compose_ui_platform_GlobalSnapshotManager$stableprop|#static{}androidx_compose_ui_platform_GlobalSnapshotManager$stableprop[0]
Expand Down Expand Up @@ -4513,6 +4514,7 @@ final fun androidx.compose.ui.platform/androidx_compose_ui_platform_DefaultUiApp
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_DefaultViewConfiguration$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_DefaultViewConfiguration$stableprop_getter|androidx_compose_ui_platform_DefaultViewConfiguration$stableprop_getter(){}[0]
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_DelegateRootForTestListener$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_DelegateRootForTestListener$stableprop_getter|androidx_compose_ui_platform_DelegateRootForTestListener$stableprop_getter(){}[0]
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_DelegatingSoftwareKeyboardController$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_DelegatingSoftwareKeyboardController$stableprop_getter|androidx_compose_ui_platform_DelegatingSoftwareKeyboardController$stableprop_getter(){}[0]
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_DisposableSaveableStateRegistry$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_DisposableSaveableStateRegistry$stableprop_getter|androidx_compose_ui_platform_DisposableSaveableStateRegistry$stableprop_getter(){}[0]
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_EmptyViewConfiguration$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_EmptyViewConfiguration$stableprop_getter|androidx_compose_ui_platform_EmptyViewConfiguration$stableprop_getter(){}[0]
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_FlushCoroutineDispatcher$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_FlushCoroutineDispatcher$stableprop_getter|androidx_compose_ui_platform_FlushCoroutineDispatcher$stableprop_getter(){}[0]
final fun androidx.compose.ui.platform/androidx_compose_ui_platform_GlobalSnapshotManager$stableprop_getter(): kotlin/Int // androidx.compose.ui.platform/androidx_compose_ui_platform_GlobalSnapshotManager$stableprop_getter|androidx_compose_ui_platform_GlobalSnapshotManager$stableprop_getter(){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.window.DialogWindowScope
import androidx.compose.ui.window.UndecoratedWindowResizer
import androidx.compose.ui.window.WindowExceptionHandler
import androidx.savedstate.SavedState
import java.awt.Component
import java.awt.ComponentOrientation
import java.awt.Frame
Expand All @@ -45,46 +46,36 @@ import org.jetbrains.skiko.SkiaLayerAnalytics
* ComposeDialog inherits javax.swing.JDialog.
*/
class ComposeDialog : JDialog {
private val skiaLayerAnalytics: SkiaLayerAnalytics
private val composePanel: ComposeWindowPanel

internal var rootForTestListener
get() = composePanel.rootForTestListener
set(value) { composePanel.rootForTestListener = value }

private fun createComposePanel() = ComposeWindowPanel(
private fun createComposePanel(
skiaLayerAnalytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
savedState: SavedState? = null,
) = ComposeWindowPanel(
window = this,
isUndecorated = ::isUndecorated,
skiaLayerAnalytics = skiaLayerAnalytics,
savedState = savedState,
)

constructor(
owner: Window?,
modalityType: ModalityType = ModalityType.MODELESS,
graphicsConfiguration: GraphicsConfiguration? = null
) : super(owner, "", modalityType, graphicsConfiguration) {
skiaLayerAnalytics = SkiaLayerAnalytics.Empty
composePanel = createComposePanel()
contentPane.add(composePanel)
}

/**
* ComposeDialog is a dialog for building UI using Compose for Desktop.
* ComposeDialog inherits javax.swing.JDialog.
*
* @param skiaLayerAnalytics Analytics that helps to know more about SkiaLayer behaviour.
* SkiaLayer is underlying class used internally to draw Compose content.
* Implementation usually uses third-party solution to send info to some centralized analytics gatherer.
* @param savedState The saved state to restore the UI state from a previous instance.
*/
@ExperimentalComposeUiApi
constructor(
owner: Window?,
modalityType: ModalityType = ModalityType.MODELESS,
graphicsConfiguration: GraphicsConfiguration? = null,
skiaLayerAnalytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty
skiaLayerAnalytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
savedState: SavedState? = null,
) : super(owner, "", modalityType, graphicsConfiguration) {
this.skiaLayerAnalytics = skiaLayerAnalytics
composePanel = createComposePanel()
composePanel = createComposePanel(skiaLayerAnalytics, savedState)
contentPane.add(composePanel)
}

Expand All @@ -95,41 +86,51 @@ class ComposeDialog : JDialog {
* @param skiaLayerAnalytics Analytics that helps to know more about SkiaLayer behaviour.
* SkiaLayer is underlying class used internally to draw Compose content.
* Implementation usually uses third-party solution to send info to some centralized analytics gatherer.
* @param savedState The saved state to restore the UI state from a previous instance.
*/
@ExperimentalComposeUiApi
constructor(
skiaLayerAnalytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty
) : super() {
this.skiaLayerAnalytics = skiaLayerAnalytics
composePanel = createComposePanel()
skiaLayerAnalytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
savedState: SavedState? = null,
): super() {
composePanel = createComposePanel(skiaLayerAnalytics, savedState)
contentPane.add(composePanel)
}

@Deprecated("Use the constructor with setting owner explicitly. Will be removed in 1.3")
constructor(
modalityType: ModalityType = ModalityType.MODELESS
) : super(null, modalityType) {
skiaLayerAnalytics = SkiaLayerAnalytics.Empty
composePanel = createComposePanel()
contentPane.add(composePanel)
}
owner: Window?,
modalityType: ModalityType = ModalityType.MODELESS,
graphicsConfiguration: GraphicsConfiguration? = null,
) : this(
owner = owner,
modalityType = modalityType,
graphicsConfiguration = graphicsConfiguration,
skiaLayerAnalytics = SkiaLayerAnalytics.Empty,
savedState = null
)

constructor(graphicsConfiguration: GraphicsConfiguration? = null) :
super(null as Frame?, "", false, graphicsConfiguration) {
skiaLayerAnalytics = SkiaLayerAnalytics.Empty
composePanel = createComposePanel()
contentPane.add(composePanel)
}
constructor(
graphicsConfiguration: GraphicsConfiguration? = null,
) : this(
owner = null,
modalityType = ModalityType.MODELESS,
graphicsConfiguration = graphicsConfiguration,
skiaLayerAnalytics = SkiaLayerAnalytics.Empty,
savedState = null
)

// don't replace super() by super(null, ModalityType.MODELESS), because
// this constructor creates an icon in the taskbar.
// Dialog's shouldn't be appeared in the taskbar.
constructor() : super() {
skiaLayerAnalytics = SkiaLayerAnalytics.Empty
composePanel = createComposePanel()
contentPane.add(composePanel)
}

internal var rootForTestListener
get() = composePanel.rootForTestListener
set(value) { composePanel.rootForTestListener = value }

private val undecoratedWindowResizer = UndecoratedWindowResizer(this)

override fun add(component: Component) = composePanel.add(component)
Expand Down Expand Up @@ -226,6 +227,17 @@ class ComposeDialog : JDialog {
*/
var undecoratedResizerThickness: Dp by undecoratedWindowResizer::resizerThickness

/**
* Saves the current UI state into a [SavedState] object. The returned state can be used
* to restore the UI state later by passing it to the constructor's `savedState` parameter.
*
* @return A [SavedState] object containing the current UI state.
*/
@ExperimentalComposeUiApi
fun saveState(): SavedState? {
return composePanel.saveState()
}

override fun dispose() {
composePanel.dispose()
super.dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.ui.awt.RenderSettings.SwingGraphics
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.scene.ComposeContainer
import androidx.compose.ui.window.WindowExceptionHandler
import androidx.savedstate.SavedState
import java.awt.Color
import java.awt.Component
import java.awt.ComponentOrientation
Expand All @@ -45,13 +46,16 @@ import org.jetbrains.skiko.SkiaLayerAnalytics
* @param skiaLayerAnalytics Analytics that helps to know more about SkiaLayer behaviour.
* SkiaLayer is underlying class used internally to draw Compose content.
* Implementation usually uses third-party solution to send info to some centralized analytics gatherer.
* @param savedState The saved state to restore the UI state from a previous instance.
* @param renderSettings Configuration class for rendering settings.
*/
class ComposePanel @ExperimentalComposeUiApi constructor(
private val skiaLayerAnalytics: SkiaLayerAnalytics,
private val skiaLayerAnalytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
private var savedState: SavedState? = null,
private val renderSettings: RenderSettings = DefaultRenderSettings
) : JLayeredPane() {
constructor() : this(
savedState = null,
skiaLayerAnalytics = SkiaLayerAnalytics.Empty,
renderSettings = DefaultRenderSettings
)
Expand Down Expand Up @@ -129,6 +133,17 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
@ExperimentalComposeUiApi
var isDisposeOnRemove: Boolean = true

/**
* Saves the current UI state into a [SavedState] object. The returned state can be used
* to restore the UI state later by passing it to the constructor's [savedState] parameter.
*
* @return A [SavedState] object containing the current UI state.
*/
@ExperimentalComposeUiApi
fun saveState(): SavedState? {
return _composeContainer?.saveState()
}

/**
* Disposes Compose state and rendering resources.
*
Expand All @@ -139,7 +154,9 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
*/
@ExperimentalComposeUiApi
fun dispose() {
_composeContainer?.dispose()
val composeContainer = _composeContainer ?: return
savedState = composeContainer.saveState()
composeContainer.dispose()
_composeContainer = null
}

Expand Down Expand Up @@ -212,6 +229,7 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
if (composeContent != null) {
it.setContent(composeContent)
}
savedState = null
}
composeContainer.addNotify()
}
Expand All @@ -220,6 +238,7 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
return ComposeContainer(
container = this,
skiaLayerAnalytics = skiaLayerAnalytics,
savedState = savedState,
windowContainer = windowContainer,
renderSettings = renderSettings,
).apply {
Expand Down
Loading