kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
refactor: consolidate sort button into `NodeFilterTextField` component
rodzic
e89f59745d
commit
8be6d74ed8
|
@ -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),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,20 +5,18 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
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.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
@ -154,26 +152,19 @@ fun NodesScreen(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
Row(
|
NodeFilterTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(MaterialTheme.colors.background)
|
|
||||||
.padding(8.dp),
|
.padding(8.dp),
|
||||||
) {
|
filterText = state.filter,
|
||||||
NodeFilterTextField(
|
onTextChange = model::setNodeFilterText,
|
||||||
filterText = state.filter,
|
currentSortOption = state.sort,
|
||||||
onTextChanged = model::setNodeFilterText,
|
onSortSelect = model::setSortOption,
|
||||||
modifier = Modifier.weight(1f)
|
includeUnknown = state.includeUnknown,
|
||||||
)
|
onToggleIncludeUnknown = model::toggleIncludeUnknown,
|
||||||
NodeSortButton(
|
showDetails = state.showDetails,
|
||||||
currentSortOption = state.sort,
|
onToggleShowDetails = model::toggleShowDetails,
|
||||||
onSortSelected = model::setSortOption,
|
)
|
||||||
includeUnknown = state.includeUnknown,
|
|
||||||
onToggleIncludeUnknown = model::toggleIncludeUnknown,
|
|
||||||
showDetails = state.showDetails,
|
|
||||||
onToggleShowDetails = model::toggleShowDetails,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
items(nodes, key = { it.num }) { node ->
|
items(nodes, key = { it.num }) { node ->
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
package com.geeksville.mesh.ui.components
|
package com.geeksville.mesh.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
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.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
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.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.OutlinedTextField
|
import androidx.compose.material.OutlinedTextField
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Clear
|
import androidx.compose.material.icons.filled.Clear
|
||||||
|
import androidx.compose.material.icons.filled.Done
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
@ -21,19 +28,55 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.onFocusEvent
|
import androidx.compose.ui.focus.onFocusEvent
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
|
import com.geeksville.mesh.model.NodeSortOption
|
||||||
import com.geeksville.mesh.ui.theme.AppTheme
|
import com.geeksville.mesh.ui.theme.AppTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NodeFilterTextField(
|
fun NodeFilterTextField(
|
||||||
filterText : String,
|
modifier: Modifier = Modifier,
|
||||||
onTextChanged : (String) -> Unit,
|
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,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
|
@ -42,8 +85,7 @@ fun NodeFilterTextField(
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.heightIn(max = 48.dp)
|
.heightIn(max = 48.dp)
|
||||||
.onFocusEvent { isFocused = it.isFocused }
|
.onFocusEvent { isFocused = it.isFocused },
|
||||||
.background(MaterialTheme.colors.background),
|
|
||||||
value = filterText,
|
value = filterText,
|
||||||
placeholder = {
|
placeholder = {
|
||||||
Text(
|
Text(
|
||||||
|
@ -58,14 +100,14 @@ fun NodeFilterTextField(
|
||||||
contentDescription = stringResource(id = R.string.node_filter_placeholder),
|
contentDescription = stringResource(id = R.string.node_filter_placeholder),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onValueChange = onTextChanged,
|
onValueChange = onTextChange,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
if (filterText.isNotEmpty() || isFocused) {
|
if (filterText.isNotEmpty() || isFocused) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Clear,
|
Icons.Default.Clear,
|
||||||
contentDescription = stringResource(id = R.string.desc_node_filter_clear),
|
contentDescription = stringResource(id = R.string.desc_node_filter_clear),
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
onTextChanged("")
|
onTextChange("")
|
||||||
focusManager.clearFocus()
|
focusManager.clearFocus()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -84,19 +126,98 @@ fun NodeFilterTextField(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewLightDark
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
fun NodeFilterTextFieldPreview() {
|
private fun NodeSortButton(
|
||||||
AppTheme {
|
currentSortOption: NodeSortOption,
|
||||||
Box(
|
onSortSelect: (NodeSortOption) -> Unit,
|
||||||
modifier = Modifier
|
includeUnknown: Boolean,
|
||||||
.fillMaxWidth()
|
onToggleIncludeUnknown: () -> Unit,
|
||||||
.background(MaterialTheme.colors.background)
|
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(
|
Text(
|
||||||
filterText = "Filter text",
|
text = stringResource(id = R.string.node_filter_include_unknown),
|
||||||
onTextChanged = { }
|
|
||||||
)
|
)
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreviewLightDark
|
||||||
|
@Composable
|
||||||
|
private fun NodeFilterTextFieldPreview() {
|
||||||
|
AppTheme {
|
||||||
|
NodeFilterTextField(
|
||||||
|
filterText = "Filter text",
|
||||||
|
onTextChange = {},
|
||||||
|
currentSortOption = NodeSortOption.LAST_HEARD,
|
||||||
|
onSortSelect = {},
|
||||||
|
includeUnknown = false,
|
||||||
|
onToggleIncludeUnknown = {},
|
||||||
|
showDetails = false,
|
||||||
|
onToggleShowDetails = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue