Skip to content
Open
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
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import org.gradle.internal.jvm.Jvm
import org.gradle.internal.os.OperatingSystem
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
import org.jetbrains.compose.ExperimentalComposeLibrary
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.desktop.application.tasks.AbstractJPackageTask
import org.jetbrains.compose.internal.de.undercouch.gradle.tasks.download.Download
Expand Down Expand Up @@ -119,6 +120,8 @@ dependencies {
implementation(libs.markdown)
implementation(libs.markdownJVM)

@OptIn(ExperimentalComposeLibrary::class)
testImplementation(compose.uiTest)
testImplementation(kotlin("test"))
testImplementation(libs.mockitoKotlin)
testImplementation(libs.junitJupiter)
Expand Down
1 change: 1 addition & 0 deletions app/src/processing/app/ui/Editor.java
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@ public void buildDevelopMenu(){
var updateTrigger = new JMenuItem(Language.text("menu.develop.check_for_updates"));
updateTrigger.addActionListener(e -> {
Preferences.unset("update.last");
Preferences.setInteger("update.beta_welcome", 0);
new UpdateCheck(base);
});
developMenu.add(updateTrigger);
Expand Down
57 changes: 12 additions & 45 deletions app/src/processing/app/ui/WelcomeToBeta.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import processing.app.Base.getVersionName
import processing.app.ui.theme.LocalLocale
import processing.app.ui.theme.LocalTheme
import processing.app.ui.theme.Locale
import processing.app.ui.theme.PDEComposeWindow
import processing.app.ui.theme.PDESwingWindow
import processing.app.ui.theme.ProcessingTheme
import java.awt.Cursor
import java.awt.Dimension
Expand All @@ -54,46 +56,20 @@ import javax.swing.SwingUtilities

class WelcomeToBeta {
companion object{
val windowSize = Dimension(400, 200)
val windowTitle = Locale()["beta.window.title"]

@JvmStatic
fun showWelcomeToBeta() {
val mac = SystemInfo.isMacFullWindowContentSupported
SwingUtilities.invokeLater {
JFrame(windowTitle).apply {
val close = {
Preferences.set("update.beta_welcome", getRevision().toString())
dispose()
}
rootPane.putClientProperty("apple.awt.transparentTitleBar", mac)
rootPane.putClientProperty("apple.awt.fullWindowContent", mac)
defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE
contentPane.add(ComposePanel().apply {
size = windowSize
setContent {
ProcessingTheme {
Box(modifier = Modifier.padding(top = if (mac) 22.dp else 0.dp)) {
welcomeToBeta(close)
}
}
}
})
pack()
background = java.awt.Color.white
setLocationRelativeTo(null)
addKeyListener(object : KeyAdapter() {
override fun keyPressed(e: KeyEvent) {
if (e.keyCode == KeyEvent.VK_ESCAPE) close()
}
})
isResizable = false
isVisible = true
requestFocus()
val close = {
Preferences.set("update.beta_welcome", getRevision().toString())
}

PDESwingWindow("beta.window.title", onClose = close) {
welcomeToBeta(close)
}
}
}

val windowSize = Dimension(400, 200)
@Composable
fun welcomeToBeta(close: () -> Unit = {}) {
Row(
Expand Down Expand Up @@ -194,18 +170,9 @@ class WelcomeToBeta {
@JvmStatic
fun main(args: Array<String>) {
application {
val windowState = rememberWindowState(
size = DpSize.Unspecified,
position = WindowPosition(Alignment.Center)
)

Window(onCloseRequest = ::exitApplication, state = windowState, title = windowTitle) {
ProcessingTheme {
Surface(color = colors.background) {
welcomeToBeta {
exitApplication()
}
}
PDEComposeWindow(titleKey = "beta.window.title", onClose = ::exitApplication){
welcomeToBeta {
exitApplication()
}
}
}
Expand Down
129 changes: 107 additions & 22 deletions app/src/processing/app/ui/theme/Locale.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
package processing.app.ui.theme

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import processing.app.LocalPreferences
import processing.app.Messages
import processing.app.Platform
import processing.app.PlatformStart
import processing.app.watchFile
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
import processing.app.*
import java.io.File
import java.io.InputStream
import java.util.*

class Locale(language: String = "") : Properties() {
/**
* The Locale class extends the standard Java Properties class
* to provide localization capabilities.
* It loads localization resources from property files based on the specified language code.
* The class also provides a method to change the current locale and update the application accordingly.
* Usage:
* ```
* val locale = Locale("es") { newLocale ->
* // Handle locale change, e.g., update UI or restart application
* }
* val localizedString = locale["someKey"]
* ```
*/
class Locale(language: String = "", val setLocale: ((java.util.Locale) -> Unit)? = null) : Properties() {
var locale: java.util.Locale = java.util.Locale.getDefault()

init {
val locale = java.util.Locale.getDefault()
load(ClassLoader.getSystemResourceAsStream("PDE.properties"))
load(ClassLoader.getSystemResourceAsStream("PDE_${locale.language}.properties") ?: InputStream.nullInputStream())
load(ClassLoader.getSystemResourceAsStream("PDE_${locale.toLanguageTag()}.properties") ?: InputStream.nullInputStream())
load(ClassLoader.getSystemResourceAsStream("PDE_${language}.properties") ?: InputStream.nullInputStream())
loadResourceUTF8("PDE.properties")
loadResourceUTF8("PDE_${locale.language}.properties")
loadResourceUTF8("PDE_${locale.toLanguageTag()}.properties")
loadResourceUTF8("PDE_${language}.properties")
}

fun loadResourceUTF8(path: String) {
val stream = ClassLoader.getSystemResourceAsStream(path)
stream?.reader(charset = Charsets.UTF_8)?.use { reader ->
load(reader)
}
}

@Deprecated("Use get instead", ReplaceWith("get(key)"))
Expand All @@ -28,18 +45,86 @@ class Locale(language: String = "") : Properties() {
return value
}
operator fun get(key: String): String = getProperty(key, key)
fun set(locale: java.util.Locale) {
setLocale?.invoke(locale)
}
}
val LocalLocale = compositionLocalOf { Locale() }
/**
* A CompositionLocal to provide access to the Locale instance
* throughout the composable hierarchy. see [LocaleProvider]
* Usage:
* ```
* val locale = LocalLocale.current
* val localizedString = locale["someKey"]
* ```
*/
val LocalLocale = compositionLocalOf<Locale> { error("No Locale Set") }

/**
* This composable function sets up a locale provider that manages application localization.
* It initializes the locale from a language file, watches for changes to that file, and updates
* the locale accordingly. It uses a [Locale] class to handle loading of localized resources.
*
* Usage:
* ```
* LocaleProvider {
* // Your app content here
* }
* ```
*
* To access the locale:
* ```
* val locale = LocalLocale.current
* val localizedString = locale["someKey"]
* ```
*
* To change the locale:
* ```
* locale.set(java.util.Locale("es"))
* ```
* This will update the `language.txt` file and reload the locale.
*/
@Composable
fun LocaleProvider(content: @Composable () -> Unit) {
PlatformStart()
val preferencesFolderOverride: File? = System.getProperty("processing.app.preferences.folder")?.let { File(it) }

val settingsFolder = preferencesFolderOverride ?: remember{
Platform.init()
Platform.getSettingsFolder()
}
val languageFile = settingsFolder.resolve("language.txt")
remember(languageFile){
if(languageFile.exists()) return@remember

val settingsFolder = Platform.getSettingsFolder()
val languageFile = File(settingsFolder, "language.txt")
watchFile(languageFile)
Messages.log("Creating language file at ${languageFile.absolutePath}")
settingsFolder.mkdirs()
languageFile.writeText(java.util.Locale.getDefault().language)
}

val update = watchFile(languageFile)
var code by remember(languageFile, update){ mutableStateOf(languageFile.readText().substring(0, 2)) }
remember(code) {
val locale = java.util.Locale(code)
java.util.Locale.setDefault(locale)
}

fun setLocale(locale: java.util.Locale) {
Messages.log("Setting locale to ${locale.language}")
languageFile.writeText(locale.language)
code = locale.language
}


val locale = Locale(code, ::setLocale)
remember(code) { Messages.log("Loaded Locale: $code") }
val dir = when(locale["locale.direction"]) {
"rtl" -> LayoutDirection.Rtl
else -> LayoutDirection.Ltr
}

val locale = Locale(languageFile.readText().substring(0, 2))
CompositionLocalProvider(LocalLocale provides locale) {
content()
CompositionLocalProvider(LocalLayoutDirection provides dir) {
CompositionLocalProvider(LocalLocale provides locale) {
content()
}
}
}
Loading