Skip to content

Commit 3e04faa

Browse files
authored
Merge pull request #1284 from Stefterv/compose-windows
Add PDE window utilities for Compose and Swing
2 parents 77eba30 + 4686345 commit 3e04faa

File tree

3 files changed

+157
-44
lines changed

3 files changed

+157
-44
lines changed

app/src/processing/app/ui/Editor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ public void buildDevelopMenu(){
10571057
var updateTrigger = new JMenuItem(Language.text("menu.develop.check_for_updates"));
10581058
updateTrigger.addActionListener(e -> {
10591059
Preferences.unset("update.last");
1060+
Preferences.setInteger("update.beta_welcome", 0);
10601061
new UpdateCheck(base);
10611062
});
10621063
developMenu.add(updateTrigger);

app/src/processing/app/ui/WelcomeToBeta.kt

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ import processing.app.Base.getRevision
4040
import processing.app.Base.getVersionName
4141
import processing.app.ui.theme.LocalLocale
4242
import processing.app.ui.theme.Locale
43-
import processing.app.ui.theme.PDETheme
43+
import processing.app.ui.theme.PDEComposeWindow
44+
import processing.app.ui.theme.PDESwingWindow
45+
import processing.app.ui.theme.ProcessingTheme
4446
import java.awt.Cursor
4547
import java.awt.Dimension
4648
import java.awt.event.KeyAdapter
@@ -53,46 +55,20 @@ import javax.swing.SwingUtilities
5355

5456
class WelcomeToBeta {
5557
companion object{
56-
val windowSize = Dimension(400, 250)
57-
val windowTitle = Locale()["beta.window.title"]
58-
5958
@JvmStatic
6059
fun showWelcomeToBeta() {
61-
val mac = SystemInfo.isMacFullWindowContentSupported
6260
SwingUtilities.invokeLater {
63-
JFrame(windowTitle).apply {
64-
val close = {
65-
Preferences.set("update.beta_welcome", getRevision().toString())
66-
dispose()
67-
}
68-
rootPane.putClientProperty("apple.awt.transparentTitleBar", mac)
69-
rootPane.putClientProperty("apple.awt.fullWindowContent", mac)
70-
defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE
71-
contentPane.add(ComposePanel().apply {
72-
size = windowSize
73-
setContent {
74-
PDETheme(darkTheme = false) {
75-
Box(modifier = Modifier.padding(top = if (mac) 22.dp else 0.dp)) {
76-
welcomeToBeta(close)
77-
}
78-
}
79-
}
80-
})
81-
pack()
82-
background = java.awt.Color.white
83-
setLocationRelativeTo(null)
84-
addKeyListener(object : KeyAdapter() {
85-
override fun keyPressed(e: KeyEvent) {
86-
if (e.keyCode == KeyEvent.VK_ESCAPE) close()
87-
}
88-
})
89-
isResizable = false
90-
isVisible = true
91-
requestFocus()
61+
val close = {
62+
Preferences.set("update.beta_welcome", getRevision().toString())
63+
}
64+
65+
PDESwingWindow("beta.window.title", onClose = close) {
66+
welcomeToBeta(close)
9267
}
9368
}
9469
}
9570

71+
val windowSize = Dimension(400, 200)
9672
@Composable
9773
fun welcomeToBeta(close: () -> Unit = {}) {
9874
Row(
@@ -151,16 +127,9 @@ class WelcomeToBeta {
151127
@JvmStatic
152128
fun main(args: Array<String>) {
153129
application {
154-
val windowState = rememberWindowState(
155-
size = windowSize.let { DpSize(it.width.dp, it.height.dp) },
156-
position = WindowPosition(Alignment.Center)
157-
)
158-
159-
Window(onCloseRequest = ::exitApplication, state = windowState, title = windowTitle) {
160-
PDETheme(darkTheme = false) {
161-
welcomeToBeta {
162-
exitApplication()
163-
}
130+
PDEComposeWindow(titleKey = "beta.window.title", onClose = ::exitApplication){
131+
welcomeToBeta {
132+
exitApplication()
164133
}
165134
}
166135
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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, onClose: () -> Unit = {}, 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) return
58+
59+
window.dispose()
60+
onClose()
61+
}
62+
})
63+
isResizable = false
64+
isVisible = true
65+
requestFocus()
66+
}
67+
}
68+
69+
/**
70+
* Internal Composable function to set up the window content with theming and localization.
71+
* It also handles macOS specific properties for full window content.
72+
*
73+
* @param window The JFrame instance to be configured.
74+
* @param titleKey The key for the window title, which will be localized.
75+
* @param fullWindowContent If true, the content will extend into the title bar area on macOS.
76+
* @param content The composable content to be displayed in the window.
77+
*/
78+
@Composable
79+
private fun PDEWindowContent(window: JFrame, titleKey: String, fullWindowContent: Boolean = false, content: @Composable BoxScope.() -> Unit){
80+
val mac = SystemInfo.isMacOS && SystemInfo.isMacFullWindowContentSupported
81+
remember {
82+
window.rootPane.putClientProperty("apple.awt.fullWindowContent", mac && fullWindowContent)
83+
window.rootPane.putClientProperty("apple.awt.transparentTitleBar", mac && fullWindowContent)
84+
}
85+
86+
CompositionLocalProvider(LocalWindow provides window) {
87+
ProcessingTheme {
88+
val locale = LocalLocale.current
89+
window.title = locale[titleKey]
90+
LaunchedEffect(locale) {
91+
window.pack()
92+
window.setLocationRelativeTo(null)
93+
}
94+
95+
Box(modifier = Modifier.padding(top = if (mac && !fullWindowContent) 22.dp else 0.dp),content = content)
96+
}
97+
}
98+
}
99+
100+
/**
101+
* A Composable function to create and display a new window with the specified content.
102+
* This function sets up the window state and handles the close request.
103+
* Use this when creating a Compose based window from another Compose context.
104+
*
105+
* Usage example:
106+
* ```
107+
* PDEComposeWindow("window.title", fullWindowContent = true, onClose = { /* handle close */ }) {
108+
* // Your window content here
109+
* Text("Hello, World!")
110+
* }
111+
* ```
112+
*
113+
* This will create a new window with the title localized from "window.title" key,
114+
* with content extending into the title bar area on macOS, and a custom close handler.
115+
*
116+
* Fully standalone example:
117+
* ```
118+
* application {
119+
* PDEComposeWindow("window.title", fullWindowContent = true, onClose = ::exitApplication) {
120+
* // Your window content here
121+
* }
122+
* }
123+
* ```
124+
*
125+
* @param titleKey The key for the window title, which will be localized.
126+
* @param fullWindowContent If true, the content will extend into the title bar area on
127+
* macOS.
128+
* @param onClose A lambda function to be called when the window is requested to close.
129+
* @param content The composable content to be displayed in the window.
130+
*
131+
*
132+
*
133+
*/
134+
@Composable
135+
fun PDEComposeWindow(titleKey: String, fullWindowContent: Boolean = false, onClose: () -> Unit = {}, content: @Composable BoxScope.() -> Unit){
136+
val windowState = rememberWindowState(
137+
size = DpSize.Unspecified,
138+
position = WindowPosition(Alignment.Center)
139+
)
140+
Window(onCloseRequest = onClose, state = windowState, title = "") {
141+
PDEWindowContent(window, titleKey, fullWindowContent, content)
142+
}
143+
}

0 commit comments

Comments
 (0)