mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
Saveable/Reactive Preferences
This commit is contained in:
@@ -2,9 +2,13 @@ package processing.app
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.dropWhile
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.nio.file.*
|
||||
import java.util.Properties
|
||||
|
||||
@@ -15,35 +19,58 @@ const val DEFAULTS_FILE_NAME = "defaults.txt"
|
||||
fun PlatformStart(){
|
||||
Platform.inst ?: Platform.init()
|
||||
}
|
||||
class ReactiveProperties: Properties() {
|
||||
val _stateMap = mutableStateMapOf<String, String>()
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
_stateMap[key] = value
|
||||
}
|
||||
|
||||
override fun getProperty(key: String): String? {
|
||||
return _stateMap[key] ?: super.getProperty(key)
|
||||
}
|
||||
|
||||
operator fun get(key: String): String? = getProperty(key)
|
||||
}
|
||||
val LocalPreferences = compositionLocalOf<ReactiveProperties> { error("No preferences provided") }
|
||||
@OptIn(FlowPreview::class)
|
||||
@Composable
|
||||
fun loadPreferences(): Properties{
|
||||
fun PreferencesProvider(content: @Composable () -> Unit){
|
||||
PlatformStart()
|
||||
|
||||
val settingsFolder = Platform.getSettingsFolder()
|
||||
val preferencesFile = settingsFolder.resolve(PREFERENCES_FILE_NAME)
|
||||
|
||||
if(!preferencesFile.exists()){
|
||||
preferencesFile.createNewFile()
|
||||
}
|
||||
watchFile(preferencesFile)
|
||||
var update by remember { mutableStateOf(System.currentTimeMillis()) }
|
||||
val update = watchFile(preferencesFile)
|
||||
val properties = remember(preferencesFile, update) { ReactiveProperties().apply {
|
||||
load((ClassLoader.getSystemResourceAsStream(DEFAULTS_FILE_NAME)?: InputStream.nullInputStream()).reader(Charsets.UTF_8))
|
||||
load(preferencesFile.inputStream().reader(Charsets.UTF_8))
|
||||
}}
|
||||
|
||||
// TODO: Make observable when preferences change
|
||||
// TODO: Save to file when preferences change
|
||||
class ObservableProperties : Properties() {
|
||||
override fun setProperty(key: String, value: String): Any? {
|
||||
update = System.currentTimeMillis()
|
||||
return super.setProperty(key, value)
|
||||
}
|
||||
val initialState = remember(properties) { properties._stateMap.toMap() }
|
||||
|
||||
LaunchedEffect(properties) {
|
||||
snapshotFlow { properties._stateMap.toMap() }
|
||||
.dropWhile { it == initialState }
|
||||
.debounce(1000)
|
||||
.collect {
|
||||
preferencesFile.outputStream().use { output ->
|
||||
output.write(
|
||||
properties.entries
|
||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.key.toString() })
|
||||
.joinToString("\n") { (key, value) -> "$key=$value" }
|
||||
.toByteArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ObservableProperties().apply {
|
||||
load(ClassLoader.getSystemResourceAsStream(DEFAULTS_FILE_NAME) ?: InputStream.nullInputStream())
|
||||
load(preferencesFile.inputStream())
|
||||
CompositionLocalProvider(LocalPreferences provides properties){
|
||||
content()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun watchFile(file: File): Any? {
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -72,12 +99,4 @@ fun watchFile(file: File): Any? {
|
||||
}
|
||||
}
|
||||
return event
|
||||
}
|
||||
val LocalPreferences = compositionLocalOf<Properties> { error("No preferences provided") }
|
||||
@Composable
|
||||
fun PreferencesProvider(content: @Composable () -> Unit){
|
||||
val preferences = loadPreferences()
|
||||
CompositionLocalProvider(LocalPreferences provides preferences){
|
||||
content()
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,9 @@ import androidx.compose.ui.window.application
|
||||
import com.charleskorn.kaml.Yaml
|
||||
import com.charleskorn.kaml.YamlConfiguration
|
||||
import kotlinx.serialization.Serializable
|
||||
import processing.app.LocalPreferences
|
||||
import processing.app.Platform
|
||||
import processing.app.loadPreferences
|
||||
import processing.app.ReactiveProperties
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import javax.swing.JFrame
|
||||
@@ -106,7 +107,7 @@ fun contributionsManager(){
|
||||
var localContributions by remember { mutableStateOf(listOf<Contribution>()) }
|
||||
var error by remember { mutableStateOf<Exception?>(null) }
|
||||
|
||||
val preferences = loadPreferences()
|
||||
val preferences = LocalPreferences.current
|
||||
|
||||
LaunchedEffect(preferences){
|
||||
try {
|
||||
@@ -284,9 +285,9 @@ fun contributionsManager(){
|
||||
}
|
||||
|
||||
|
||||
fun loadContributionProperties(preferences: Properties): List<Pair<Type, Properties>>{
|
||||
fun loadContributionProperties(preferences: ReactiveProperties): List<Pair<Type, Properties>>{
|
||||
val result = mutableListOf<Pair<Type, Properties>>()
|
||||
val sketchBook = Path(preferences.getProperty("sketchbook.path.four", Platform.getDefaultSketchbookFolder().path))
|
||||
val sketchBook = Path(preferences.getProperty("sketchbook.path.four") ?: Platform.getDefaultSketchbookFolder().path)
|
||||
sketchBook.forEachDirectoryEntry{ contributionsFolder ->
|
||||
if(!contributionsFolder.isDirectory()) return@forEachDirectoryEntry
|
||||
val typeName = contributionsFolder.fileName.toString()
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.Properties
|
||||
class Theme(themeFile: String? = "") : Properties() {
|
||||
init {
|
||||
load(ClassLoader.getSystemResourceAsStream("theme.txt"))
|
||||
load(ClassLoader.getSystemResourceAsStream(themeFile) ?: InputStream.nullInputStream())
|
||||
load(ClassLoader.getSystemResourceAsStream(themeFile ?: "") ?: InputStream.nullInputStream())
|
||||
}
|
||||
fun getColor(key: String): Color {
|
||||
return Color(getProperty(key).toColorInt())
|
||||
|
||||
Reference in New Issue
Block a user