@@ -2,9 +2,13 @@ package processing.app
2
2
3
3
import androidx.compose.runtime.*
4
4
import kotlinx.coroutines.Dispatchers
5
+ import kotlinx.coroutines.FlowPreview
6
+ import kotlinx.coroutines.flow.debounce
7
+ import kotlinx.coroutines.flow.dropWhile
5
8
import kotlinx.coroutines.launch
6
9
import java.io.File
7
10
import java.io.InputStream
11
+ import java.io.OutputStream
8
12
import java.nio.file.*
9
13
import java.util.Properties
10
14
@@ -15,35 +19,58 @@ const val DEFAULTS_FILE_NAME = "defaults.txt"
15
19
fun PlatformStart (){
16
20
Platform .inst ? : Platform .init ()
17
21
}
22
+ class ReactiveProperties : Properties () {
23
+ val _stateMap = mutableStateMapOf<String , String >()
18
24
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 )
19
38
@Composable
20
- fun loadPreferences (): Properties {
39
+ fun PreferencesProvider ( content : @Composable () -> Unit ) {
21
40
PlatformStart ()
22
41
23
42
val settingsFolder = Platform .getSettingsFolder()
24
43
val preferencesFile = settingsFolder.resolve(PREFERENCES_FILE_NAME )
25
44
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
+ } }
31
50
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
+ }
39
67
}
40
68
41
- return ObservableProperties ().apply {
42
- load(ClassLoader .getSystemResourceAsStream(DEFAULTS_FILE_NAME ) ? : InputStream .nullInputStream())
43
- load(preferencesFile.inputStream())
69
+ CompositionLocalProvider (LocalPreferences provides properties){
70
+ content()
44
71
}
45
- }
46
72
73
+ }
47
74
@Composable
48
75
fun watchFile (file : File ): Any? {
49
76
val scope = rememberCoroutineScope()
@@ -72,12 +99,4 @@ fun watchFile(file: File): Any? {
72
99
}
73
100
}
74
101
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
- }
83
102
}
0 commit comments