Skip to content

Commit 58c746b

Browse files
committed
Add PDE window utilities for Compose and Swing
Introduces PDESwingWindow and PDEComposeWindow classes to simplify creating themed and localized windows in Compose and Swing applications. Includes macOS-specific handling for full window content and localization support for window titles.
1 parent d42fb2f commit 58c746b

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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

Comments
 (0)