From e4ec46bfae09217e47cba787375c2957a59f4760 Mon Sep 17 00:00:00 2001 From: MFlisar Date: Mon, 9 Sep 2024 09:56:20 +0200 Subject: [PATCH] added filtering + expose a state for the current hierarchical level --- .idea/.gitignore | 3 + .idea/deploymentTargetDropDown.xml | 10 -- .idea/deploymentTargetSelector.xml | 10 ++ .idea/inspectionProfiles/Project_Default.xml | 41 ----- .idea/jarRepositories.xml | 35 ----- .idea/kotlinc.xml | 2 +- .idea/misc.xml | 1 + demo/build.gradle.kts | 1 + .../composepreferences/demo/MainActivity.kt | 4 +- .../demo/demos/PrefScreenDemoFilter.kt | 141 ++++++++++++++++++ gradle/compose.versions.toml | 2 +- gradle/libs.versions.toml | 5 +- library/core/build.gradle.kts | 1 + .../composepreferences/core/PreferenceInfo.kt | 39 ++++- .../core/PreferenceScreen.kt | 7 +- .../core/classes/PreferenceFilter.kt | 56 +++++++ .../core/classes/PreferenceSettings.kt | 25 +++- .../core/composables/BasePreference.kt | 5 +- .../composables/BasePreferenceContainer.kt | 21 ++- .../core/helper/SearchText.kt | 113 ++++++++++++++ .../core/hierarchy/HierarchyAwareRoot.kt | 8 + .../core/hierarchy/PreferenceItem.kt | 6 +- .../modules/kotpreferences/build.gradle.kts | 1 + library/modules/screen/bool/build.gradle.kts | 1 + .../modules/screen/button/build.gradle.kts | 1 + library/modules/screen/color/build.gradle.kts | 1 + library/modules/screen/date/build.gradle.kts | 1 + library/modules/screen/input/build.gradle.kts | 1 + library/modules/screen/list/build.gradle.kts | 1 + .../modules/screen/number/build.gradle.kts | 1 + library/modules/screen/time/build.gradle.kts | 1 + 31 files changed, 435 insertions(+), 110 deletions(-) create mode 100644 .idea/.gitignore delete mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 .idea/deploymentTargetSelector.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/jarRepositories.xml create mode 100644 demo/src/main/java/com/michaelflisar/composepreferences/demo/demos/PrefScreenDemoFilter.kt create mode 100644 library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceFilter.kt create mode 100644 library/core/src/main/java/com/michaelflisar/composepreferences/core/helper/SearchText.kt diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml deleted file mode 100644 index 81d2a46..0000000 --- a/.idea/deploymentTargetDropDown.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..82d64ee --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 44ca2d9..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 96b7c65..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fe63bb6..d4b7acc 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8978d23..0ad17cb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index 67ebf1c..7eb366a 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.application") id("kotlin-android") id("kotlin-parcelize") + alias(libs.plugins.compose.compiler) } android { diff --git a/demo/src/main/java/com/michaelflisar/composepreferences/demo/MainActivity.kt b/demo/src/main/java/com/michaelflisar/composepreferences/demo/MainActivity.kt index 6b179fb..0976dbb 100644 --- a/demo/src/main/java/com/michaelflisar/composepreferences/demo/MainActivity.kt +++ b/demo/src/main/java/com/michaelflisar/composepreferences/demo/MainActivity.kt @@ -40,6 +40,7 @@ import com.michaelflisar.composepreferences.demo.classes.LocalDataStore import com.michaelflisar.composepreferences.demo.composables.MyInfoLine import com.michaelflisar.composepreferences.demo.demos.PrefScreenCustomDemo import com.michaelflisar.composepreferences.demo.demos.PrefScreenDemo +import com.michaelflisar.composepreferences.demo.demos.PrefScreenDemoFilter import com.michaelflisar.composepreferences.demo.demos.PrefScreenDemoKotPreferences1 import com.michaelflisar.composepreferences.kotpreferences.asPreferenceData import com.michaelflisar.composepreferences.screen.bool.PreferenceBool @@ -106,12 +107,13 @@ private fun Content( when (page.value) { 0 -> Root( page, theme, dynamicTheme, expandedRootRegions, - listOf("Demo", "Demo KotPreference", "Demo Custom") + listOf("Demo", "Demo KotPreference", "Demo Custom", "Demo Filter") ) 1 -> PrefScreenDemo() 2 -> PrefScreenDemoKotPreferences1() 3 -> PrefScreenCustomDemo() + 4 -> PrefScreenDemoFilter() } } } diff --git a/demo/src/main/java/com/michaelflisar/composepreferences/demo/demos/PrefScreenDemoFilter.kt b/demo/src/main/java/com/michaelflisar/composepreferences/demo/demos/PrefScreenDemoFilter.kt new file mode 100644 index 0000000..cbdca82 --- /dev/null +++ b/demo/src/main/java/com/michaelflisar/composepreferences/demo/demos/PrefScreenDemoFilter.kt @@ -0,0 +1,141 @@ +package com.michaelflisar.composepreferences.demo.demos + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.michaelflisar.composepreferences.core.PreferenceInfo +import com.michaelflisar.composepreferences.core.PreferenceScreen +import com.michaelflisar.composepreferences.core.PreferenceSectionHeader +import com.michaelflisar.composepreferences.core.PreferenceSubScreen +import com.michaelflisar.composepreferences.core.classes.PreferenceFilter +import com.michaelflisar.composepreferences.core.classes.rememberPreferenceFilter +import com.michaelflisar.composepreferences.core.hierarchy.PreferenceScope +import com.michaelflisar.composepreferences.demo.classes.DemoPrefs +import com.michaelflisar.kotpreferences.core.initialisation.SettingSetup + +@Composable +fun PrefScreenDemoFilter() { + + val settings = DemoPrefs.preferenceSettings() + val filter = rememberPreferenceFilter( + mode = PreferenceFilter.Mode.AllWords, + highlightSpan = SpanStyle(color = Color.Red) + ) + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp), + value = filter.search.value, + onValueChange = { filter.search.value = it }, + label = { Text("Search") }, + trailingIcon = if (filter.search.value.isNotEmpty()) { + { + IconButton(onClick = { filter.search.value = "" }) { + Icon( + Icons.Default.Clear, + contentDescription = "clear" + ) + } + } + } else null + ) + + PreferenceScreen( + settings = settings, + filter = filter + ) { + PreferenceSectionHeader( + title = { Text("Demos") } + ) + PreferenceInfoExamples() + } + } +} + +@Composable +private fun PreferenceScope.PreferenceInfoExamples() { + PreferenceSubScreen( + title = { Text("Infos") }, + subtitle = { Text("Click to see some info preference examples") }, + icon = { Icon(Icons.Outlined.Info, null) } + ) { + PreferenceSectionHeader( + title = { Text("Infos") } + ) + + // either use the TEXT BASED OVERLOADS (those will add all texts to the filter tags list) + // or provide a list of manually defined filter tags + PreferenceInfo(title = "Info 1", subtitle = "Color") + PreferenceInfo( + title = "Info 2", + subtitle = "Color2", + filterTags = listOf("red", "green", "blue") + ) + PreferenceInfo(title = "Info 3") + PreferenceInfo(title = "Info 4") + PreferenceInfo(title = "Info 5") + PreferenceInfo(title = "Info 6") + + PreferenceInfo( + title = { Text("Info 7") }, + filterTags = listOf("Info 7") + ) + + PreferenceSubScreen( + title = { Text("Sub Infos") }, + icon = { Icon(Icons.Outlined.Info, null) } + ) { + PreferenceInfo(title = "Sub Info 1", subtitle = "Theme", filterTags = listOf("style")) + PreferenceInfo(title = "Sub Info 2") + PreferenceInfo(title = "Sub Info 3") + PreferenceInfo(title = "Sub Info 4") + PreferenceInfo(title = "Sub Info 5") + PreferenceInfo(title = "Sub Info 6") + + PreferenceSubScreen( + title = { Text("Sub Sub Infos") }, + icon = { Icon(Icons.Outlined.Info, null) } + ) { + PreferenceInfo(title = "Sub Sub Info 1") + PreferenceInfo(title = "Sub Sub Info 2") + PreferenceInfo(title = "Sub Sub Info 3") + } + } + } +} + +@Composable +@Preview(showBackground = true) +fun Preview() { + SettingSetup.init(LocalContext.current) + MaterialTheme { + Surface( + modifier = Modifier + .height(400.dp) + ) { + PrefScreenDemoFilter() + } + } +} \ No newline at end of file diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index e8073d5..294d94c 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,6 +1,6 @@ [versions] composeBom = "2024.04.00" -compiler = "1.5.11" +compiler = "1.5.15" activity = "1.8.2" accompanist = "0.32.0" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bb7120..0e21a18 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,11 @@ [versions] -gradle = "8.3.2" -kotlin = "1.9.23" +gradle = "8.6.0" +kotlin = "2.0.20" kotlinx-coroutines = "1.8.0" [plugins] +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } [libraries] diff --git a/library/core/build.gradle.kts b/library/core/build.gradle.kts index e3f8d18..2f00a49 100644 --- a/library/core/build.gradle.kts +++ b/library/core/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("kotlin-android") id("kotlin-parcelize") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceInfo.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceInfo.kt index 96d3e35..f2770ec 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceInfo.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceInfo.kt @@ -3,22 +3,19 @@ package com.michaelflisar.composepreferences.core import android.content.res.Configuration import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Info -import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.Alignment import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import com.michaelflisar.composepreferences.core.classes.Dependency import com.michaelflisar.composepreferences.core.classes.LocalPreferenceSettings import com.michaelflisar.composepreferences.core.classes.PreferenceStyle -import com.michaelflisar.composepreferences.core.classes.PreferenceStyleDefaults import com.michaelflisar.composepreferences.core.composables.BasePreference import com.michaelflisar.composepreferences.core.composables.PreferenceItemSetup -import com.michaelflisar.composepreferences.core.composables.PreferenceItemSetupDefaults import com.michaelflisar.composepreferences.core.composables.PreviewPreference +import com.michaelflisar.composepreferences.core.helper.SearchText import com.michaelflisar.composepreferences.core.hierarchy.PreferenceScope /** @@ -41,7 +38,8 @@ fun PreferenceScope.PreferenceInfo( subtitle: @Composable (() -> Unit)? = null, icon: (@Composable () -> Unit)? = null, preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle, - itemSetup: PreferenceItemSetup = PreferenceInfoDefaults.itemSetup() + itemSetup: PreferenceItemSetup = PreferenceInfoDefaults.itemSetup(), + filterTags: List = emptyList() ) { BasePreference( itemSetup = itemSetup, @@ -52,7 +50,36 @@ fun PreferenceScope.PreferenceInfo( icon = icon, onLongClick = onLongClick, preferenceStyle = preferenceStyle, - content = null + content = null, + filterTags = filterTags + ) +} + +@Composable +fun PreferenceScope.PreferenceInfo( + // Special + onLongClick: (() -> Unit)? = null, + // Base Preference + title: String, + enabled: Dependency = Dependency.Enabled, + visible: Dependency = Dependency.Enabled, + subtitle: String? = null, + icon: (@Composable () -> Unit)? = null, + preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle, + itemSetup: PreferenceItemSetup = PreferenceInfoDefaults.itemSetup(), + filterTags: List = emptyList() +) { + BasePreference( + itemSetup = itemSetup, + enabled = enabled, + visible = visible, + title = { SearchText(title) }, + subtitle = subtitle?.let { { SearchText(subtitle) } }, + icon = icon, + onLongClick = onLongClick, + preferenceStyle = preferenceStyle, + content = null, + filterTags = filterTags + listOfNotNull(title, subtitle) ) } diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceScreen.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceScreen.kt index 9494ca8..eebc9c1 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceScreen.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/PreferenceScreen.kt @@ -3,7 +3,9 @@ package com.michaelflisar.composepreferences.core import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier +import com.michaelflisar.composepreferences.core.classes.LocalPreferenceFilter import com.michaelflisar.composepreferences.core.classes.LocalPreferenceSettings +import com.michaelflisar.composepreferences.core.classes.PreferenceFilter import com.michaelflisar.composepreferences.core.classes.PreferenceSettings import com.michaelflisar.composepreferences.core.classes.PreferenceSettingsDefaults import com.michaelflisar.composepreferences.core.classes.PreferenceStyleDefaults @@ -16,6 +18,7 @@ import com.michaelflisar.composepreferences.core.hierarchy.PreferenceRootScope * @param modifier the [Modifier] for this composable * @param scrollable if true, this composable does wrap its content inside a scrollable container * @param settings the [PreferenceSettings] for this screen - use [PreferenceSettingsDefaults.settings] to provide your own settings + * @param filter the [PreferenceFilter] for this screen - use [rememberPreferenceFilter] to provide your setup * @param content the content of this screen */ @Composable @@ -23,10 +26,12 @@ fun PreferenceScreen( modifier: Modifier = Modifier, scrollable: Boolean = true, settings: PreferenceSettings = PreferenceSettingsDefaults.settings(), + filter: PreferenceFilter? = null, content: @Composable PreferenceRootScope.() -> Unit ) { CompositionLocalProvider( - LocalPreferenceSettings provides settings + LocalPreferenceSettings provides settings, + LocalPreferenceFilter provides filter ) { HierarchyAwareRoot( scrollable = scrollable, diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceFilter.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceFilter.kt new file mode 100644 index 0000000..f88711c --- /dev/null +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceFilter.kt @@ -0,0 +1,56 @@ +package com.michaelflisar.composepreferences.core.classes + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.text.SpanStyle + +val LocalPreferenceFilter = compositionLocalOf { null } + +class PreferenceFilter internal constructor( + val search: MutableState, + val ignoreCase: Boolean, + val mode: Mode, + val highlightSpan: SpanStyle +) { + enum class Mode { + Exakt, + AllWords, + AnyWord + } + + companion object { + internal fun words(search: String) = search.split(" ").map { it.trim() }.filter { it.isNotEmpty() } + } + + internal fun filter(search: String, tags: List): Boolean { + if (tags.isEmpty() && search.isEmpty()) { + return true + } + return when (mode) { + Mode.Exakt -> tags.any { it.contains(search, ignoreCase) } + Mode.AllWords, + Mode.AnyWord -> { + val matches = words(search).map { word -> + tags.any { it.contains(word, ignoreCase) } + } + if (mode == Mode.AllWords) { + !matches.contains(false) + } else matches.contains(true) + } + } + } +} + +@Composable +fun rememberPreferenceFilter( + search: String = "", + ignoreCase: Boolean = true, + mode: PreferenceFilter.Mode = PreferenceFilter.Mode.AllWords, + highlightSpan: SpanStyle = SpanStyle(color = MaterialTheme.colorScheme.primary) + +): PreferenceFilter = + PreferenceFilter(rememberSaveable { mutableStateOf(search) }, ignoreCase, mode, highlightSpan) \ No newline at end of file diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceSettings.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceSettings.kt index b76a865..2a0ba37 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceSettings.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/classes/PreferenceSettings.kt @@ -5,10 +5,13 @@ import androidx.compose.animation.core.FastOutLinearInEasing import androidx.compose.animation.core.tween import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight -import androidx.compose.material.icons.filled.KeyboardArrowRight import androidx.compose.material3.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -16,6 +19,10 @@ val LocalPreferenceSettings = compositionLocalOf { PreferenceSettings() } /** * see [PreferenceSettingsDefaults.settings] + * + * additionally this class exposes following: + * + * currentLevel... this state holds the currently visible hierarchical level to determine if you are at the root level or not */ data class PreferenceSettings internal constructor( val disabledStateAlpha: Float = .4f, @@ -33,8 +40,12 @@ data class PreferenceSettings internal constructor( val forceNoIconInset: Boolean = false, val minTextAreaWidth: Dp = 48.dp, val leadingContentEndPadding: Dp = 16.dp, - val trailingContentStartPadding: Dp = 16.dp -) + val trailingContentStartPadding: Dp = 16.dp, + internal val level: MutableState = mutableIntStateOf(0) +) { + val currentLevel: State + get() = level +} object PreferenceSettingsDefaults { @@ -64,7 +75,10 @@ object PreferenceSettingsDefaults { easing = FastOutLinearInEasing ), subScreenEndIndicator: @Composable (() -> Unit)? = { - Icon(imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) + Icon( + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, + contentDescription = null + ) }, itemStyle: PreferenceStyle = PreferenceStyleDefaults.item(), forceNoIconInset: Boolean = false, @@ -82,6 +96,7 @@ object PreferenceSettingsDefaults { forceNoIconInset = forceNoIconInset, minTextAreaWidth = minTextAreaWidth, leadingContentEndPadding = leadingContentEndPadding, - trailingContentStartPadding = trailingContentStartPadding + trailingContentStartPadding = trailingContentStartPadding, + level = remember { mutableIntStateOf(0) } ) } diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreference.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreference.kt index 374892b..bd57d49 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreference.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreference.kt @@ -3,7 +3,6 @@ package com.michaelflisar.composepreferences.core.composables import androidx.compose.foundation.layout.ColumnScope import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import com.michaelflisar.composepreferences.core.classes.Dependency import com.michaelflisar.composepreferences.core.classes.LocalPreferenceSettings import com.michaelflisar.composepreferences.core.classes.PreferenceStyle @@ -43,6 +42,7 @@ fun PreferenceScope.BasePreference( preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle, itemSetup: PreferenceItemSetup = PreferenceItemSetup(), settings: PreferenceItemSettings = PreferenceItemSettings(), + filterTags: List = emptyList(), content: (@Composable ColumnScope.() -> Unit)? = null ) { BasePreferenceContainer( @@ -52,7 +52,8 @@ fun PreferenceScope.BasePreference( onClick = onClick, onLongClick = onLongClick, group = settings.group, - preferenceStyle = preferenceStyle + preferenceStyle = preferenceStyle, + filterTags = filterTags ) { modifier -> PreferenceItem( modifier = modifier, diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreferenceContainer.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreferenceContainer.kt index b9fbec3..849d453 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreferenceContainer.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/composables/BasePreferenceContainer.kt @@ -7,8 +7,9 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.graphicsLayer @@ -16,6 +17,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import com.michaelflisar.composepreferences.core.classes.Dependency +import com.michaelflisar.composepreferences.core.classes.LocalPreferenceFilter import com.michaelflisar.composepreferences.core.classes.LocalPreferenceSettings import com.michaelflisar.composepreferences.core.classes.PreferenceStyle import com.michaelflisar.composepreferences.core.classes.PreferenceStyleDefaults @@ -52,11 +54,24 @@ fun PreferenceScope.BasePreferenceContainer( onLongClick: (() -> Unit)? = null, group: Boolean = false, preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle, + filterTags: List = emptyList(), content: @Composable (modifier: Modifier) -> Unit ) { val stateVisible = visible.state() val stateEnabled = enabled.state() val settings = LocalPreferenceSettings.current + val filter = LocalPreferenceFilter.current + + val visibleInFilter by remember(filter?.search?.value, filterTags) { + derivedStateOf { + filter?.filter(filter.search.value, filterTags) ?: true + } + } + val visible by remember(stateVisible.value, visibleInFilter) { + derivedStateOf { + visibleInFilter && stateVisible.value + } + } //println("stateVisible = $stateVisible | containerColor = ${preferenceStyle.colors.containerColor()}") @@ -65,7 +80,7 @@ fun PreferenceScope.BasePreferenceContainer( Animatable(1f) } LaunchedEffect(Unit) { - if (stateVisible.value && settings.animationSpec != null) { + if (visible && settings.animationSpec != null) { animationFactor.animateTo( 0f, animationSpec = settings.animationSpec @@ -74,7 +89,7 @@ fun PreferenceScope.BasePreferenceContainer( } AnimatedPreference( - visible = stateVisible.value + visible = visible ) { val animationOffsetY = with(LocalDensity.current) { (animationFactor.value * 8).dp.toPx() } diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/helper/SearchText.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/helper/SearchText.kt new file mode 100644 index 0000000..ad3d28c --- /dev/null +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/helper/SearchText.kt @@ -0,0 +1,113 @@ +package com.michaelflisar.composepreferences.core.helper + +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import com.michaelflisar.composepreferences.core.PreferenceInfo +import com.michaelflisar.composepreferences.core.PreferenceScreen +import com.michaelflisar.composepreferences.core.classes.LocalPreferenceFilter +import com.michaelflisar.composepreferences.core.classes.PreferenceFilter +import com.michaelflisar.composepreferences.core.classes.rememberPreferenceFilter + +@Composable +fun SearchText( + text: String +) { + val filter = LocalPreferenceFilter.current + if (filter == null) { + Text(text) + } else { + val annotated by remember(text, filter) { + derivedStateOf { + buildHighlightedString(text, filter) + } + } + Text(annotated) + } +} + +private fun buildHighlightedString( + text: String, + filter: PreferenceFilter +): AnnotatedString { + return buildAnnotatedString { + append(text) + val parts = when (filter.mode) { + PreferenceFilter.Mode.Exakt -> listOf(filter.search.value) + PreferenceFilter.Mode.AllWords, + PreferenceFilter.Mode.AnyWord -> PreferenceFilter.words(filter.search.value) + } + for (part in parts) { + var offset = 0 + var startIndex = 0 + while (text.indexOf(part, startIndex = offset, ignoreCase = filter.ignoreCase) + .also { startIndex = it } != -1 + ) { + val endIndex = startIndex + part.length + addStyle(style = filter.highlightSpan, start = startIndex, end = endIndex) + offset = endIndex + } + } + } +} + +@Composable +@Preview(showBackground = true) +fun Preview() { + MaterialTheme { + Surface { + Column { + val highlightSpan = SpanStyle(color = Color.Red, fontWeight = FontWeight.Bold) + val filter1 = rememberPreferenceFilter( + "ha yo", + mode = PreferenceFilter.Mode.Exakt, + highlightSpan = highlightSpan + ) + val filter2 = rememberPreferenceFilter( + "ha yo", + mode = PreferenceFilter.Mode.AnyWord, + highlightSpan = highlightSpan + ) + val filter3 = rememberPreferenceFilter( + "ha yo", + mode = PreferenceFilter.Mode.AllWords, + highlightSpan = highlightSpan + ) + val filter4 = rememberPreferenceFilter( + "ha yo 4c", + mode = PreferenceFilter.Mode.AllWords, + highlightSpan = highlightSpan + ) + PreferenceScreen(filter = filter1, scrollable = false) { + PreferenceInfo(title = "Hallo 1", subtitle = "Hallo you") + } + PreferenceScreen(filter = filter2, scrollable = false) { + PreferenceInfo(title = "Hallo 2a", subtitle = "Hallo") + PreferenceInfo(title = "Hallo 2b", subtitle = "Hallo you") + PreferenceInfo(title = "Hallo 2c", subtitle = "Hallo you") + } + PreferenceScreen(filter = filter3, scrollable = false) { + PreferenceInfo(title = "Hallo 3a", subtitle = "Hallo") + PreferenceInfo(title = "Hallo 3b", subtitle = "Hallo you") + PreferenceInfo(title = "Hallo 3c", subtitle = "Hallo you") + } + PreferenceScreen(filter = filter4, scrollable = false) { + PreferenceInfo(title = "Hallo 4a", subtitle = "Hallo") + PreferenceInfo(title = "Hallo 4b", subtitle = "Hallo you") + PreferenceInfo(title = "Hallo 4c", subtitle = "Hallo you") + } + } + } + } +} \ No newline at end of file diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/HierarchyAwareRoot.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/HierarchyAwareRoot.kt index fcace30..73b862a 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/HierarchyAwareRoot.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/HierarchyAwareRoot.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.Immutable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.saveable.listSaver @@ -15,6 +16,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Modifier +import com.michaelflisar.composepreferences.core.classes.LocalPreferenceSettings internal data class Level(val level: Int = 0) @@ -76,6 +78,11 @@ internal fun HierarchyAwareRoot( ) { val openedGroup = rememberOpenedGroups() + val settings = LocalPreferenceSettings.current + LaunchedEffect(openedGroup.size) { + settings.level.value = openedGroup.size + } + BackHandler(openedGroup.size > 0) { openedGroup.removeLast() } @@ -99,6 +106,7 @@ internal fun HierarchyAwareRoot( LocalIndex provides Index(0), LocalOpenedGroups provides openedGroup ) { + Column( modifier = modifier .then( diff --git a/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/PreferenceItem.kt b/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/PreferenceItem.kt index cff58dd..862c8db 100644 --- a/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/PreferenceItem.kt +++ b/library/core/src/main/java/com/michaelflisar/composepreferences/core/hierarchy/PreferenceItem.kt @@ -4,12 +4,14 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import com.michaelflisar.composepreferences.core.classes.LocalPreferenceFilter @Composable internal fun PreferenceScope.PreferenceItem( group: Boolean = false, content: @Composable PreferenceItemScopeInstance.() -> Unit ) { + val filter = LocalPreferenceFilter.current val openedGroupIndizes = LocalOpenedGroups.current.toList() val openedGroupLevel = openedGroupIndizes.size - 1 @@ -19,9 +21,9 @@ internal fun PreferenceScope.PreferenceItem( val hierarchyData = HierarchyData(parents, index) - val visible by remember(level, openedGroupLevel, openedGroupIndizes) { + val visible by remember(level, openedGroupLevel, openedGroupIndizes, filter?.search?.value) { derivedStateOf { - level == openedGroupLevel + 1 && openedGroupIndizes == parents + filter?.search?.value?.isNotEmpty() == true || (level == openedGroupLevel + 1 && openedGroupIndizes == parents) } } diff --git a/library/modules/kotpreferences/build.gradle.kts b/library/modules/kotpreferences/build.gradle.kts index 670e682..8d8d1d4 100644 --- a/library/modules/kotpreferences/build.gradle.kts +++ b/library/modules/kotpreferences/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/bool/build.gradle.kts b/library/modules/screen/bool/build.gradle.kts index 00e24b1..0593512 100644 --- a/library/modules/screen/bool/build.gradle.kts +++ b/library/modules/screen/bool/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/button/build.gradle.kts b/library/modules/screen/button/build.gradle.kts index eb827cd..ca1ed65 100644 --- a/library/modules/screen/button/build.gradle.kts +++ b/library/modules/screen/button/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/color/build.gradle.kts b/library/modules/screen/color/build.gradle.kts index c7d62af..add8467 100644 --- a/library/modules/screen/color/build.gradle.kts +++ b/library/modules/screen/color/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/date/build.gradle.kts b/library/modules/screen/date/build.gradle.kts index 6a03aa5..26e6e79 100644 --- a/library/modules/screen/date/build.gradle.kts +++ b/library/modules/screen/date/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/input/build.gradle.kts b/library/modules/screen/input/build.gradle.kts index f50cd27..fbb66d6 100644 --- a/library/modules/screen/input/build.gradle.kts +++ b/library/modules/screen/input/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/list/build.gradle.kts b/library/modules/screen/list/build.gradle.kts index 05c7094..92db582 100644 --- a/library/modules/screen/list/build.gradle.kts +++ b/library/modules/screen/list/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/number/build.gradle.kts b/library/modules/screen/number/build.gradle.kts index 2ce2c9e..4da2c31 100644 --- a/library/modules/screen/number/build.gradle.kts +++ b/library/modules/screen/number/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android { diff --git a/library/modules/screen/time/build.gradle.kts b/library/modules/screen/time/build.gradle.kts index c6b20cb..34891a6 100644 --- a/library/modules/screen/time/build.gradle.kts +++ b/library/modules/screen/time/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.library") id("kotlin-android") id("maven-publish") + alias(libs.plugins.compose.compiler) } android {