diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1bfdc..cc88271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ are no binary releases; "version" tracks the shared `version` in the root build ### Added +- **`core-prefs`** — `Preferences[pref]` and `MutablePreferences[pref] = value`, + so an app can read several prefs out of one `PrefStore.data` snapshot and + write several in one `PrefStore.edit` without dropping to raw keys. Plus + `nullableBooleanPref`, for a tri-state flag where unset differs from `false`. - **`components`** — picker-row helpers lifted from Calendula's pickers and settings, all additive. `GroupedRow` takes `checked` (default `false`): it implies `selected` and fills an empty trailing slot with `SelectedCheck`, diff --git a/core-prefs/src/main/kotlin/de/jeanlucmakiola/floret/prefs/Pref.kt b/core-prefs/src/main/kotlin/de/jeanlucmakiola/floret/prefs/Pref.kt index 785660b..cc7c7cc 100644 --- a/core-prefs/src/main/kotlin/de/jeanlucmakiola/floret/prefs/Pref.kt +++ b/core-prefs/src/main/kotlin/de/jeanlucmakiola/floret/prefs/Pref.kt @@ -54,10 +54,20 @@ fun stringSetPref(name: String, default: Set = emptySet()): Pref = nullablePref(stringPreferencesKey(name), name) +/** Absent reads back as `null`; writing `null` removes the key. */ +fun nullableBooleanPref(name: String): Pref = + nullablePref(booleanPreferencesKey(name), name) + /** Absent reads back as `null`; writing `null` removes the key. */ fun nullableLongPref(name: String): Pref = nullablePref(longPreferencesKey(name), name) +/** [pref]'s value in this snapshot, for reading several prefs in one `PrefStore.data` map. */ +operator fun Preferences.get(pref: Pref): T = pref.read(this) + +/** Writes [pref] inside a multi-key `PrefStore.edit`, with the same encoding as `PrefStore.set`. */ +operator fun MutablePreferences.set(pref: Pref, value: T) = pref.write(this, value) + /** An absent key reads back as [default]; the value is stored under [key] verbatim. */ private fun scalarPref(key: Preferences.Key, name: String, default: T): Pref = Pref( diff --git a/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefStoreTest.kt b/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefStoreTest.kt index ea34046..7d0360e 100644 --- a/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefStoreTest.kt +++ b/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefStoreTest.kt @@ -281,4 +281,45 @@ class PrefStoreTest { assertThat(store.get(pref)).isFalse() } + + @Test + fun `a nullable boolean keeps false apart from unset`(@TempDir tempDir: Path) = runTest { + val store = newStore(tempDir) + val pref = nullableBooleanPref("b") + assertThat(store.get(pref)).isNull() + + store.set(pref, false) + assertThat(store.get(pref)).isFalse() + + store.set(pref, null) + assertThat(store.get(pref)).isNull() + } + + @Test + fun `several prefs read from one snapshot`(@TempDir tempDir: Path) = runTest { + val store = newStore(tempDir) + val count = intPref("count", 1) + val mode = enumPref("mode", ThemeMode.SYSTEM) + store.set(mode, ThemeMode.DARK) + + val snapshot = store.data.first() + + assertThat(snapshot[count] to snapshot[mode]).isEqualTo(1 to ThemeMode.DARK) + } + + @Test + fun `a multi-key edit writes typed prefs with their codecs`(@TempDir tempDir: Path) = runTest { + val store = newStore(tempDir) + val mode = enumPref("mode", ThemeMode.SYSTEM) + val folder = nullableStringPref("folder") + store.set(folder, "content://x") + + store.edit { + it[mode] = ThemeMode.BLACK + it[folder] = null + } + + assertThat(store.data.first()[stringPreferencesKey("mode")]).isEqualTo("BLACK") + assertThat(store.get(folder)).isNull() + } } diff --git a/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefTest.kt b/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefTest.kt index 4ec96d3..1ba43fb 100644 --- a/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefTest.kt +++ b/core-prefs/src/test/kotlin/de/jeanlucmakiola/floret/prefs/PrefTest.kt @@ -46,11 +46,11 @@ class PrefTest { @Test fun `the nullable factories default to null`() { - val prefs = listOf(nullableStringPref("s"), nullableLongPref("l")) + val prefs = listOf(nullableStringPref("s"), nullableLongPref("l"), nullableBooleanPref("b")) val defaults = prefs.map { it.default } - assertThat(defaults).containsExactly(null, null) + assertThat(defaults).containsExactly(null, null, null) } @Test