1+ package processing.app.ui.theme
2+
3+ import androidx.compose.foundation.layout.*
4+ import androidx.compose.runtime.Composable
5+ import androidx.compose.runtime.CompositionLocalProvider
6+ import androidx.compose.runtime.LaunchedEffect
7+ import androidx.compose.runtime.compositionLocalOf
8+ import androidx.compose.runtime.remember
9+ import androidx.compose.ui.Alignment
10+ import androidx.compose.ui.Modifier
11+ import androidx.compose.ui.awt.ComposePanel
12+ import androidx.compose.ui.unit.DpSize
13+ import androidx.compose.ui.unit.dp
14+ import androidx.compose.ui.window.Window
15+ import androidx.compose.ui.window.WindowPosition
16+ import androidx.compose.ui.window.rememberWindowState
17+ import com.formdev.flatlaf.util.SystemInfo
18+
19+ import java.awt.event.KeyAdapter
20+ import java.awt.event.KeyEvent
21+ import javax.swing.JFrame
22+
23+ val LocalWindow = compositionLocalOf<JFrame > { error(" No Window Set" ) }
24+
25+ /* *
26+ * A utility class to create a new Window with Compose content in a Swing application.
27+ * It sets up the window with some default properties and allows for custom content.
28+ * Use this when creating a Compose based window from Swing.
29+ *
30+ * Usage example:
31+ * ```
32+ * SwingUtilities.invokeLater {
33+ * PDESwingWindow("menu.help.welcome", fullWindowContent = true) {
34+ *
35+ * }
36+ * }
37+ * ```
38+ *
39+ * @param titleKey The key for the window title, which will be localized.
40+ * @param fullWindowContent If true, the content will extend into the title bar area on macOS.
41+ * @param content The composable content to be displayed in the window.
42+ */
43+ class PDESwingWindow (titleKey : String = " " , fullWindowContent : Boolean = false , content : @Composable BoxScope .() -> Unit ): JFrame(){
44+ init {
45+ val window = this
46+ defaultCloseOperation = DISPOSE_ON_CLOSE
47+ ComposePanel ().apply {
48+ setContent {
49+ PDEWindowContent (window, titleKey, fullWindowContent, content)
50+ }
51+ window.add(this )
52+ }
53+ background = java.awt.Color .white
54+ setLocationRelativeTo(null )
55+ addKeyListener(object : KeyAdapter () {
56+ override fun keyPressed (e : KeyEvent ) {
57+ if (e.keyCode == KeyEvent .VK_ESCAPE ) window.dispose()
58+ }
59+ })
60+ isResizable = false
61+ isVisible = true
62+ requestFocus()
63+ }
64+ }
65+
66+ /* *
67+ * Internal Composable function to set up the window content with theming and localization.
68+ * It also handles macOS specific properties for full window content.
69+ *
70+ * @param window The JFrame instance to be configured.
71+ * @param titleKey The key for the window title, which will be localized.
72+ * @param fullWindowContent If true, the content will extend into the title bar area on macOS.
73+ * @param content The composable content to be displayed in the window.
74+ */
75+ @Composable
76+ private fun PDEWindowContent (window : JFrame , titleKey : String , fullWindowContent : Boolean = false, content : @Composable BoxScope .() -> Unit ){
77+ val mac = SystemInfo .isMacOS && SystemInfo .isMacFullWindowContentSupported
78+ remember {
79+ window.rootPane.putClientProperty(" apple.awt.fullWindowContent" , mac && fullWindowContent)
80+ window.rootPane.putClientProperty(" apple.awt.transparentTitleBar" , mac && fullWindowContent)
81+ }
82+
83+ CompositionLocalProvider (LocalWindow provides window) {
84+ ProcessingTheme {
85+ val locale = LocalLocale .current
86+ window.title = locale[titleKey]
87+ LaunchedEffect (locale) {
88+ window.pack()
89+ window.setLocationRelativeTo(null )
90+ }
91+
92+ Box (modifier = Modifier .padding(top = if (mac && ! fullWindowContent) 22 .dp else 0 .dp),content = content)
93+ }
94+ }
95+ }
96+
97+ /* *
98+ * A Composable function to create and display a new window with the specified content.
99+ * This function sets up the window state and handles the close request.
100+ * Use this when creating a Compose based window from another Compose context.
101+ *
102+ * Usage example:
103+ * ```
104+ * PDEComposeWindow("window.title", fullWindowContent = true, onClose = { /* handle close */ }) {
105+ * // Your window content here
106+ * Text("Hello, World!")
107+ * }
108+ * ```
109+ *
110+ * This will create a new window with the title localized from "window.title" key,
111+ * with content extending into the title bar area on macOS, and a custom close handler.
112+ *
113+ * Fully standalone example:
114+ * ```
115+ * application {
116+ * PDEComposeWindow("window.title", fullWindowContent = true, onClose = ::exitApplication) {
117+ * // Your window content here
118+ * }
119+ * }
120+ * ```
121+ *
122+ * @param titleKey The key for the window title, which will be localized.
123+ * @param fullWindowContent If true, the content will extend into the title bar area on
124+ * macOS.
125+ * @param onClose A lambda function to be called when the window is requested to close.
126+ * @param content The composable content to be displayed in the window.
127+ *
128+ *
129+ *
130+ */
131+ @Composable
132+ fun PDEComposeWindow (titleKey : String , fullWindowContent : Boolean = false, onClose : () -> Unit = {}, content : @Composable BoxScope .() -> Unit ){
133+ val windowState = rememberWindowState(
134+ size = DpSize .Unspecified ,
135+ position = WindowPosition (Alignment .Center )
136+ )
137+ Window (onCloseRequest = onClose, state = windowState, title = " " ) {
138+ PDEWindowContent (window, titleKey, fullWindowContent, content)
139+ }
140+ }
0 commit comments