1818
1919package org.kiwix.kiwixmobile.core.ui.components
2020
21+ import androidx.compose.foundation.background
22+ import androidx.compose.foundation.clickable
2123import androidx.compose.foundation.isSystemInDarkTheme
24+ import androidx.compose.foundation.layout.Column
25+ import androidx.compose.foundation.layout.Row
26+ import androidx.compose.foundation.layout.fillMaxWidth
27+ import androidx.compose.foundation.layout.heightIn
28+ import androidx.compose.foundation.layout.padding
2229import androidx.compose.foundation.text.KeyboardActions
2330import androidx.compose.foundation.text.KeyboardOptions
2431import androidx.compose.material3.Icon
@@ -29,20 +36,40 @@ import androidx.compose.material3.TextField
2936import androidx.compose.material3.TextFieldDefaults
3037import androidx.compose.material3.minimumInteractiveComponentSize
3138import androidx.compose.runtime.Composable
39+ import androidx.compose.runtime.MutableState
3240import androidx.compose.runtime.SideEffect
41+ import androidx.compose.runtime.mutableStateOf
42+ import androidx.compose.runtime.remember
43+ import androidx.compose.ui.Alignment
3344import androidx.compose.ui.Modifier
3445import androidx.compose.ui.focus.FocusRequester
3546import androidx.compose.ui.focus.focusRequester
47+ import androidx.compose.ui.geometry.Rect
3648import androidx.compose.ui.graphics.Color
49+ import androidx.compose.ui.layout.onGloballyPositioned
50+ import androidx.compose.ui.layout.positionInRoot
3751import androidx.compose.ui.platform.LocalSoftwareKeyboardController
3852import androidx.compose.ui.platform.testTag
3953import androidx.compose.ui.res.painterResource
4054import androidx.compose.ui.res.stringResource
55+ import androidx.compose.ui.text.AnnotatedString
56+ import androidx.compose.ui.text.SpanStyle
4157import androidx.compose.ui.text.TextStyle
58+ import androidx.compose.ui.text.buildAnnotatedString
59+ import androidx.compose.ui.text.font.FontWeight
4260import androidx.compose.ui.text.input.ImeAction
4361import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis
62+ import androidx.compose.ui.text.withStyle
63+ import androidx.compose.ui.unit.IntOffset
64+ import androidx.compose.ui.unit.toSize
65+ import androidx.compose.ui.window.Popup
4466import org.kiwix.kiwixmobile.core.R
67+ import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
4568import org.kiwix.kiwixmobile.core.utils.ComposeDimens
69+ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
70+ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.MINIMUM_HEIGHT_OF_SEARCH_ITEM
71+ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SEARCH_ITEM_TEXT_SIZE
72+ import kotlin.math.roundToInt
4673
4774@Composable
4875fun KiwixSearchView (
@@ -51,9 +78,94 @@ fun KiwixSearchView(
5178 placeholder : String = stringResource(R .string.search_label),
5279 searchViewTextFiledTestTag : String = "",
5380 clearButtonTestTag : String = "",
81+ suggestionText : String? = null,
82+ onSuggestedWordClick : (String ) -> Unit = {},
5483 onValueChange : (String ) -> Unit ,
5584 onClearClick : () -> Unit ,
5685 onKeyboardSubmitButtonClick : (String ) -> Unit = {}
86+ ) {
87+ val textFieldBounds = remember { mutableStateOf(Rect .Zero ) }
88+ Column {
89+ SearchViewTextFiled (
90+ modifier,
91+ searchViewTextFiledTestTag,
92+ value,
93+ placeholder,
94+ onValueChange,
95+ onClearClick,
96+ clearButtonTestTag,
97+ onKeyboardSubmitButtonClick,
98+ textFieldBounds
99+ )
100+ ShowCorrectWordSuggestion (suggestionText, onSuggestedWordClick, textFieldBounds)
101+ }
102+ }
103+
104+ @Composable
105+ private fun ShowCorrectWordSuggestion (
106+ suggestionText : String? ,
107+ onSuggestedWordClick : (String ) -> Unit ,
108+ textFieldBounds : MutableState <Rect >
109+ ) {
110+ if (suggestionText.isNullOrBlank()) return
111+ Popup (
112+ alignment = Alignment .TopStart ,
113+ offset = IntOffset (
114+ x = textFieldBounds.value.left.roundToInt(),
115+ y = textFieldBounds.value.bottom.roundToInt()
116+ )
117+ ) {
118+ Row (
119+ modifier = Modifier
120+ .fillMaxWidth()
121+ .heightIn(min = MINIMUM_HEIGHT_OF_SEARCH_ITEM )
122+ .background(MaterialTheme .colorScheme.background)
123+ .clickable { onSuggestedWordClick(suggestionText) },
124+ verticalAlignment = Alignment .CenterVertically
125+ ) {
126+ Text (
127+ text = getSuggestedHighlightedText(suggestionText),
128+ modifier = Modifier
129+ .padding(horizontal = EIGHT_DP )
130+ .weight(1f ),
131+ fontSize = SEARCH_ITEM_TEXT_SIZE
132+ )
133+ Icon (
134+ painter = painterResource(id = R .drawable.ic_open_in_new_24dp),
135+ contentDescription = stringResource(id = R .string.suggested_search_icon_description),
136+ modifier = Modifier .padding(horizontal = EIGHT_DP )
137+ )
138+ }
139+ }
140+ }
141+
142+ @Composable
143+ private fun getSuggestedHighlightedText (suggestionText : String ): AnnotatedString {
144+ val rawString = stringResource(R .string.suggest_search_text)
145+ val parts = rawString.split(" %s" )
146+ val before = parts.getOrNull(ZERO ).orEmpty()
147+ val after = parts.getOrNull(ONE ).orEmpty()
148+ return buildAnnotatedString {
149+ append(before)
150+ withStyle(SpanStyle (fontWeight = FontWeight .Bold )) {
151+ append(suggestionText)
152+ }
153+ append(after)
154+ }
155+ }
156+
157+ @Suppress(" LongParameterList" , " LongMethod" )
158+ @Composable
159+ private fun SearchViewTextFiled (
160+ modifier : Modifier ,
161+ searchViewTextFiledTestTag : String ,
162+ value : String ,
163+ placeholder : String ,
164+ onValueChange : (String ) -> Unit ,
165+ onClearClick : () -> Unit ,
166+ clearButtonTestTag : String ,
167+ onKeyboardSubmitButtonClick : (String ) -> Unit ,
168+ textFieldBounds : MutableState <Rect >
57169) {
58170 val hintColor = if (isSystemInDarkTheme()) {
59171 Color .LightGray
@@ -75,7 +187,17 @@ fun KiwixSearchView(
75187 modifier = modifier
76188 .testTag(searchViewTextFiledTestTag)
77189 .minimumInteractiveComponentSize()
78- .focusRequester(focusRequester),
190+ .focusRequester(focusRequester)
191+ .onGloballyPositioned { coordinates ->
192+ val position = coordinates.positionInRoot()
193+ val size = coordinates.size.toSize()
194+ textFieldBounds.value = Rect (
195+ position.x,
196+ position.y,
197+ position.x + size.width,
198+ position.y + size.height
199+ )
200+ },
79201 singleLine = true ,
80202 value = value,
81203 placeholder = {
0 commit comments