diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeSortButton.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeSortButton.kt deleted file mode 100644 index dacaef8f..00000000 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeSortButton.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.geeksville.mesh.ui - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Divider -import androidx.compose.material.DropdownMenu -import androidx.compose.material.DropdownMenuItem -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Done -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import com.geeksville.mesh.R -import com.geeksville.mesh.model.NodeSortOption - -@Suppress("LongMethod") -@Composable -internal fun NodeSortButton( - currentSortOption: NodeSortOption, - onSortSelected: (NodeSortOption) -> Unit, - includeUnknown: Boolean, - onToggleIncludeUnknown: () -> Unit, - showDetails: Boolean, - onToggleShowDetails: () -> Unit, - modifier: Modifier = Modifier, -) { - Box(modifier) { - var expanded by remember { mutableStateOf(false) } - - IconButton(onClick = { expanded = true }) { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_twotone_sort_24), - contentDescription = null, - modifier = Modifier.heightIn(max = 48.dp), - tint = MaterialTheme.colors.onSurface - ) - } - - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.background(MaterialTheme.colors.background.copy(alpha = 1f)) - ) { - NodeSortOption.entries.forEach { sort -> - DropdownMenuItem( - onClick = { - onSortSelected(sort) - expanded = false - }, - ) { - Text( - text = stringResource(id = sort.stringRes), - fontWeight = if (sort == currentSortOption) FontWeight.Bold else null, - ) - } - } - Divider() - DropdownMenuItem( - onClick = { - onToggleIncludeUnknown() - expanded = false - }, - ) { - Text( - text = stringResource(id = R.string.node_filter_include_unknown), - ) - AnimatedVisibility(visible = includeUnknown) { - Icon( - imageVector = Icons.Default.Done, - contentDescription = null, - modifier = Modifier.padding(start = 4.dp), - ) - } - } - Divider() - DropdownMenuItem( - onClick = { - onToggleShowDetails() - expanded = false - }, - ) { - Text( - text = stringResource(id = R.string.node_filter_show_details), - ) - AnimatedVisibility(visible = showDetails) { - Icon( - imageVector = Icons.Default.Done, - contentDescription = null, - modifier = Modifier.padding(start = 4.dp), - ) - } - } - } - } -} diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt index eedd5eb9..382feeca 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt @@ -5,20 +5,18 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.unit.dp import androidx.fragment.app.activityViewModels @@ -154,26 +152,19 @@ fun NodesScreen( modifier = Modifier.fillMaxSize(), ) { stickyHeader { - Row( + NodeFilterTextField( modifier = Modifier .fillMaxWidth() - .background(MaterialTheme.colors.background) .padding(8.dp), - ) { - NodeFilterTextField( - filterText = state.filter, - onTextChanged = model::setNodeFilterText, - modifier = Modifier.weight(1f) - ) - NodeSortButton( - currentSortOption = state.sort, - onSortSelected = model::setSortOption, - includeUnknown = state.includeUnknown, - onToggleIncludeUnknown = model::toggleIncludeUnknown, - showDetails = state.showDetails, - onToggleShowDetails = model::toggleShowDetails, - ) - } + filterText = state.filter, + onTextChange = model::setNodeFilterText, + currentSortOption = state.sort, + onSortSelect = model::setSortOption, + includeUnknown = state.includeUnknown, + onToggleIncludeUnknown = model::toggleIncludeUnknown, + showDetails = state.showDetails, + onToggleShowDetails = model::toggleShowDetails, + ) } items(nodes, key = { it.num }) { node -> diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt index 49b851de..8b66bd0b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt @@ -1,18 +1,25 @@ package com.geeksville.mesh.ui.components +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Divider +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem import androidx.compose.material.Icon +import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -21,19 +28,55 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusEvent +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.geeksville.mesh.R +import com.geeksville.mesh.model.NodeSortOption import com.geeksville.mesh.ui.theme.AppTheme @Composable fun NodeFilterTextField( - filterText : String, - onTextChanged : (String) -> Unit, + modifier: Modifier = Modifier, + filterText: String, + onTextChange: (String) -> Unit, + currentSortOption: NodeSortOption, + onSortSelect: (NodeSortOption) -> Unit, + includeUnknown: Boolean, + onToggleIncludeUnknown: () -> Unit, + showDetails: Boolean, + onToggleShowDetails: () -> Unit, +) { + Row( + modifier = modifier.background(MaterialTheme.colors.background), + ) { + NodeFilterTextField( + filterText = filterText, + onTextChange = onTextChange, + modifier = Modifier.weight(1f) + ) + + NodeSortButton( + currentSortOption = currentSortOption, + onSortSelect = onSortSelect, + includeUnknown = includeUnknown, + onToggleIncludeUnknown = onToggleIncludeUnknown, + showDetails = showDetails, + onToggleShowDetails = onToggleShowDetails, + ) + } +} + +@Composable +private fun NodeFilterTextField( + filterText: String, + onTextChange: (String) -> Unit, modifier: Modifier = Modifier, ) { val focusManager = LocalFocusManager.current @@ -42,8 +85,7 @@ fun NodeFilterTextField( OutlinedTextField( modifier = modifier .heightIn(max = 48.dp) - .onFocusEvent { isFocused = it.isFocused } - .background(MaterialTheme.colors.background), + .onFocusEvent { isFocused = it.isFocused }, value = filterText, placeholder = { Text( @@ -58,14 +100,14 @@ fun NodeFilterTextField( contentDescription = stringResource(id = R.string.node_filter_placeholder), ) }, - onValueChange = onTextChanged, + onValueChange = onTextChange, trailingIcon = { if (filterText.isNotEmpty() || isFocused) { Icon( Icons.Default.Clear, contentDescription = stringResource(id = R.string.desc_node_filter_clear), modifier = Modifier.clickable { - onTextChanged("") + onTextChange("") focusManager.clearFocus() } ) @@ -84,19 +126,98 @@ fun NodeFilterTextField( ) } -@PreviewLightDark +@Suppress("LongMethod") @Composable -fun NodeFilterTextFieldPreview() { - AppTheme { - Box( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colors.background) +private fun NodeSortButton( + currentSortOption: NodeSortOption, + onSortSelect: (NodeSortOption) -> Unit, + includeUnknown: Boolean, + onToggleIncludeUnknown: () -> Unit, + showDetails: Boolean, + onToggleShowDetails: () -> Unit, + modifier: Modifier = Modifier, +) = Box(modifier) { + var expanded by remember { mutableStateOf(false) } + + IconButton(onClick = { expanded = true }) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_twotone_sort_24), + contentDescription = null, + modifier = Modifier.heightIn(max = 48.dp), + tint = MaterialTheme.colors.onSurface + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.background(MaterialTheme.colors.background.copy(alpha = 1f)) + ) { + NodeSortOption.entries.forEach { sort -> + DropdownMenuItem( + onClick = { + onSortSelect(sort) + expanded = false + }, + ) { + Text( + text = stringResource(id = sort.stringRes), + fontWeight = if (sort == currentSortOption) FontWeight.Bold else null, + ) + } + } + Divider() + DropdownMenuItem( + onClick = { + onToggleIncludeUnknown() + expanded = false + }, ) { - NodeFilterTextField( - filterText = "Filter text", - onTextChanged = { } + Text( + text = stringResource(id = R.string.node_filter_include_unknown), ) + AnimatedVisibility(visible = includeUnknown) { + Icon( + imageVector = Icons.Default.Done, + contentDescription = null, + modifier = Modifier.padding(start = 4.dp), + ) + } + } + Divider() + DropdownMenuItem( + onClick = { + onToggleShowDetails() + expanded = false + }, + ) { + Text( + text = stringResource(id = R.string.node_filter_show_details), + ) + AnimatedVisibility(visible = showDetails) { + Icon( + imageVector = Icons.Default.Done, + contentDescription = null, + modifier = Modifier.padding(start = 4.dp), + ) + } } } -} \ No newline at end of file +} + +@PreviewLightDark +@Composable +private fun NodeFilterTextFieldPreview() { + AppTheme { + NodeFilterTextField( + filterText = "Filter text", + onTextChange = {}, + currentSortOption = NodeSortOption.LAST_HEARD, + onSortSelect = {}, + includeUnknown = false, + onToggleIncludeUnknown = {}, + showDetails = false, + onToggleShowDetails = {}, + ) + } +}