@@ -2,38 +2,82 @@ 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
1115
1216const val PREFERENCES_FILE_NAME = " preferences.txt"
1317const val DEFAULTS_FILE_NAME = " defaults.txt"
1418
15- fun PlatformStart (){
16- Platform .inst ? : Platform .init ()
17- }
19+ class ReactiveProperties : Properties () {
20+ val _stateMap = mutableStateMapOf<String , String >()
21+
22+ override fun setProperty (key : String , value : String ) {
23+ super .setProperty(key, value)
24+ _stateMap [key] = value
25+ }
1826
27+ override fun getProperty (key : String ): String? {
28+ return _stateMap [key] ? : super .getProperty(key)
29+ }
30+
31+ operator fun get (key : String ): String? = getProperty(key)
32+
33+ operator fun set (key : String , value : String ) {
34+ setProperty(key, value)
35+ }
36+ }
37+ val LocalPreferences = compositionLocalOf<ReactiveProperties > { error(" No preferences provided" ) }
38+ @OptIn(FlowPreview ::class )
1939@Composable
20- fun loadPreferences (): Properties {
21- PlatformStart ()
40+ fun PreferencesProvider (content : @Composable () -> Unit ){
41+ remember {
42+ Platform .init ()
43+ }
2244
2345 val settingsFolder = Platform .getSettingsFolder()
2446 val preferencesFile = settingsFolder.resolve(PREFERENCES_FILE_NAME )
25-
2647 if (! preferencesFile.exists()){
48+ preferencesFile.mkdirs()
2749 preferencesFile.createNewFile()
2850 }
29- watchFile(preferencesFile)
3051
31- return Properties ().apply {
32- load(ClassLoader .getSystemResourceAsStream(DEFAULTS_FILE_NAME ) ? : InputStream .nullInputStream())
33- load(preferencesFile.inputStream())
52+ val update = watchFile(preferencesFile)
53+ val properties = remember(preferencesFile, update) { ReactiveProperties ().apply {
54+ load((ClassLoader .getSystemResourceAsStream(DEFAULTS_FILE_NAME )? : InputStream .nullInputStream()).reader(Charsets .UTF_8 ))
55+ load(preferencesFile.inputStream().reader(Charsets .UTF_8 ))
56+ }}
57+
58+ val initialState = remember(properties) { properties._stateMap .toMap() }
59+
60+ LaunchedEffect (properties) {
61+ snapshotFlow { properties._stateMap .toMap() }
62+ .dropWhile { it == initialState }
63+ .debounce(100 )
64+ .collect {
65+ preferencesFile.outputStream().use { output ->
66+ output.write(
67+ properties.entries
68+ .sortedWith(compareBy(String .CASE_INSENSITIVE_ORDER ) { it.key.toString() })
69+ .joinToString(" \n " ) { (key, value) -> " $key =$value " }
70+ .toByteArray()
71+ )
72+ }
73+ }
74+ }
75+
76+ CompositionLocalProvider (LocalPreferences provides properties){
77+ content()
3478 }
35- }
3679
80+ }
3781@Composable
3882fun watchFile (file : File ): Any? {
3983 val scope = rememberCoroutineScope()
@@ -63,12 +107,4 @@ fun watchFile(file: File): Any? {
63107 }
64108 }
65109 return event
66- }
67- val LocalPreferences = compositionLocalOf<Properties > { error(" No preferences provided" ) }
68- @Composable
69- fun PreferencesProvider (content : @Composable () -> Unit ){
70- val preferences = loadPreferences()
71- CompositionLocalProvider (LocalPreferences provides preferences){
72- content()
73- }
74110}
0 commit comments