@@ -2,9 +2,13 @@ package processing.app
22
33import androidx.compose.runtime.*
44import kotlinx.coroutines.Dispatchers
5+ import kotlinx.coroutines.FlowPreview
6+ import kotlinx.coroutines.flow.debounce
7+ import kotlinx.coroutines.flow.dropWhile
58import kotlinx.coroutines.launch
69import java.io.File
710import java.io.InputStream
11+ import java.io.OutputStream
812import java.nio.file.*
913import java.util.Properties
1014
@@ -15,35 +19,58 @@ const val DEFAULTS_FILE_NAME = "defaults.txt"
1519fun PlatformStart (){
1620 Platform .inst ? : Platform .init ()
1721}
22+ class ReactiveProperties : Properties () {
23+ val _stateMap = mutableStateMapOf<String , String >()
1824
25+ override fun setProperty (key : String , value : String ) {
26+ super .setProperty(key, value)
27+ _stateMap [key] = value
28+ }
29+
30+ override fun getProperty (key : String ): String? {
31+ return _stateMap [key] ? : super .getProperty(key)
32+ }
33+
34+ operator fun get (key : String ): String? = getProperty(key)
35+ }
36+ val LocalPreferences = compositionLocalOf<ReactiveProperties > { error(" No preferences provided" ) }
37+ @OptIn(FlowPreview ::class )
1938@Composable
20- fun loadPreferences (): Properties {
39+ fun PreferencesProvider ( content : @Composable () -> Unit ) {
2140 PlatformStart ()
2241
2342 val settingsFolder = Platform .getSettingsFolder()
2443 val preferencesFile = settingsFolder.resolve(PREFERENCES_FILE_NAME )
2544
26- if ( ! preferencesFile.exists()){
27- preferencesFile.createNewFile()
28- }
29- watchFile (preferencesFile)
30- var update by remember { mutableStateOf( System .currentTimeMillis()) }
45+ val update = watchFile( preferencesFile)
46+ val properties = remember( preferencesFile, update) { ReactiveProperties (). apply {
47+ load(( ClassLoader .getSystemResourceAsStream( DEFAULTS_FILE_NAME ) ? : InputStream .nullInputStream()).reader( Charsets . UTF_8 ))
48+ load (preferencesFile.inputStream().reader( Charsets . UTF_8 ) )
49+ } }
3150
32- // TODO: Make observable when preferences change
33- // TODO: Save to file when preferences change
34- class ObservableProperties : Properties () {
35- override fun setProperty (key : String , value : String ): Any? {
36- update = System .currentTimeMillis()
37- return super .setProperty(key, value)
38- }
51+ val initialState = remember(properties) { properties._stateMap .toMap() }
52+
53+ LaunchedEffect (properties) {
54+ snapshotFlow { properties._stateMap .toMap() }
55+ .dropWhile { it == initialState }
56+ .debounce(1000 )
57+ .collect {
58+ preferencesFile.outputStream().use { output ->
59+ output.write(
60+ properties.entries
61+ .sortedWith(compareBy(String .CASE_INSENSITIVE_ORDER ) { it.key.toString() })
62+ .joinToString(" \n " ) { (key, value) -> " $key =$value " }
63+ .toByteArray()
64+ )
65+ }
66+ }
3967 }
4068
41- return ObservableProperties ().apply {
42- load(ClassLoader .getSystemResourceAsStream(DEFAULTS_FILE_NAME ) ? : InputStream .nullInputStream())
43- load(preferencesFile.inputStream())
69+ CompositionLocalProvider (LocalPreferences provides properties){
70+ content()
4471 }
45- }
4672
73+ }
4774@Composable
4875fun watchFile (file : File ): Any? {
4976 val scope = rememberCoroutineScope()
@@ -72,12 +99,4 @@ fun watchFile(file: File): Any? {
7299 }
73100 }
74101 return event
75- }
76- val LocalPreferences = compositionLocalOf<Properties > { error(" No preferences provided" ) }
77- @Composable
78- fun PreferencesProvider (content : @Composable () -> Unit ){
79- val preferences = loadPreferences()
80- CompositionLocalProvider (LocalPreferences provides preferences){
81- content()
82- }
83102}
0 commit comments