DropdownField (ConfigScreen) and PluginProviderDropdown (PluginsScreen) were byte-for-byte identical. Extract into ui/common/ExposedDropdownField and route both call sites to it. No behavior change. Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
package com.m57.hermescontrol.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared dropdown field used by [com.m57.hermescontrol.ui.config.ConfigScreen]
|
||||||
|
* and [com.m57.hermescontrol.ui.plugins.PluginsScreen]. Extracted from the
|
||||||
|
* byte-for-byte identical private copies that previously lived in each screen
|
||||||
|
* (issue #796).
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ExposedDropdownField(
|
||||||
|
label: String,
|
||||||
|
options: List<String>,
|
||||||
|
selectedValue: String,
|
||||||
|
onOptionSelected: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
ExposedDropdownMenuBox(
|
||||||
|
expanded = expanded,
|
||||||
|
onExpandedChange = { expanded = it },
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = selectedValue.ifEmpty { options.firstOrNull() ?: "" },
|
||||||
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
|
||||||
|
label = { Text(label) },
|
||||||
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||||
|
singleLine = true,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
ExposedDropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
) {
|
||||||
|
options.forEach { option ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(option) },
|
||||||
|
onClick = {
|
||||||
|
onOptionSelected(option)
|
||||||
|
expanded = false
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,11 +23,7 @@ import androidx.compose.material3.Button
|
|||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
|
||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -41,7 +37,6 @@ import androidx.compose.material3.TextButton
|
|||||||
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.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -57,6 +52,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import com.m57.hermescontrol.R
|
import com.m57.hermescontrol.R
|
||||||
import com.m57.hermescontrol.data.model.SchemaField
|
import com.m57.hermescontrol.data.model.SchemaField
|
||||||
import com.m57.hermescontrol.ui.common.ErrorState
|
import com.m57.hermescontrol.ui.common.ErrorState
|
||||||
|
import com.m57.hermescontrol.ui.common.ExposedDropdownField
|
||||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||||
import com.m57.hermescontrol.ui.common.NavIcon
|
import com.m57.hermescontrol.ui.common.NavIcon
|
||||||
import com.m57.hermescontrol.ui.common.SearchBar
|
import com.m57.hermescontrol.ui.common.SearchBar
|
||||||
@@ -591,7 +587,7 @@ private fun ConfigField(
|
|||||||
}
|
}
|
||||||
|
|
||||||
"select" -> {
|
"select" -> {
|
||||||
DropdownField(
|
ExposedDropdownField(
|
||||||
label = label,
|
label = label,
|
||||||
options = field.options ?: emptyList(),
|
options = field.options ?: emptyList(),
|
||||||
selectedValue = (currentValue as? JsonPrimitive)?.content ?: "",
|
selectedValue = (currentValue as? JsonPrimitive)?.content ?: "",
|
||||||
@@ -655,51 +651,6 @@ private fun ConfigField(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun DropdownField(
|
|
||||||
label: String,
|
|
||||||
options: List<String>,
|
|
||||||
selectedValue: String,
|
|
||||||
onOptionSelected: (String) -> Unit,
|
|
||||||
) {
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
|
||||||
expanded = expanded,
|
|
||||||
onExpandedChange = { expanded = it },
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = selectedValue.ifEmpty { options.firstOrNull() ?: "" },
|
|
||||||
onValueChange = {},
|
|
||||||
readOnly = true,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
|
|
||||||
label = { Text(label) },
|
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
|
||||||
singleLine = true,
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenu(
|
|
||||||
expanded = expanded,
|
|
||||||
onDismissRequest = { expanded = false },
|
|
||||||
) {
|
|
||||||
options.forEach { option ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(option) },
|
|
||||||
onClick = {
|
|
||||||
onOptionSelected(option)
|
|
||||||
expanded = false
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Recursively find all config dot-paths not covered by the schema. */
|
/** Recursively find all config dot-paths not covered by the schema. */
|
||||||
private fun collectUncoveredPaths(
|
private fun collectUncoveredPaths(
|
||||||
config: Map<String, JsonElement>,
|
config: Map<String, JsonElement>,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
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.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.InstallDesktop
|
import androidx.compose.material.icons.filled.InstallDesktop
|
||||||
@@ -24,11 +23,7 @@ import androidx.compose.material3.Button
|
|||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
|
||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -56,6 +51,7 @@ import com.m57.hermescontrol.theme.LocalHermesStatusColors
|
|||||||
import com.m57.hermescontrol.ui.common.DetailDialog
|
import com.m57.hermescontrol.ui.common.DetailDialog
|
||||||
import com.m57.hermescontrol.ui.common.EmptyState
|
import com.m57.hermescontrol.ui.common.EmptyState
|
||||||
import com.m57.hermescontrol.ui.common.ErrorState
|
import com.m57.hermescontrol.ui.common.ErrorState
|
||||||
|
import com.m57.hermescontrol.ui.common.ExposedDropdownField
|
||||||
import com.m57.hermescontrol.ui.common.HermesScaffold
|
import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||||
import com.m57.hermescontrol.ui.common.NavIcon
|
import com.m57.hermescontrol.ui.common.NavIcon
|
||||||
import com.m57.hermescontrol.ui.common.SearchBar
|
import com.m57.hermescontrol.ui.common.SearchBar
|
||||||
@@ -247,7 +243,7 @@ private fun ProviderSelectionSection(
|
|||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
PluginProviderDropdown(
|
ExposedDropdownField(
|
||||||
label = stringResource(R.string.plugins_memory_provider_label),
|
label = stringResource(R.string.plugins_memory_provider_label),
|
||||||
options =
|
options =
|
||||||
listOf(
|
listOf(
|
||||||
@@ -276,7 +272,7 @@ private fun ProviderSelectionSection(
|
|||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
PluginProviderDropdown(
|
ExposedDropdownField(
|
||||||
label = stringResource(R.string.plugins_context_engine_label),
|
label = stringResource(R.string.plugins_context_engine_label),
|
||||||
options = listOf("compressor") + state.contextOptions.map { it.name },
|
options = listOf("compressor") + state.contextOptions.map { it.name },
|
||||||
selectedValue = state.contextEngine,
|
selectedValue = state.contextEngine,
|
||||||
@@ -302,51 +298,6 @@ private fun ProviderSelectionSection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun PluginProviderDropdown(
|
|
||||||
label: String,
|
|
||||||
options: List<String>,
|
|
||||||
selectedValue: String,
|
|
||||||
onOptionSelected: (String) -> Unit,
|
|
||||||
) {
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
|
||||||
expanded = expanded,
|
|
||||||
onExpandedChange = { expanded = it },
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = selectedValue.ifEmpty { options.firstOrNull() ?: "" },
|
|
||||||
onValueChange = {},
|
|
||||||
readOnly = true,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
|
|
||||||
label = { Text(label) },
|
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
|
||||||
singleLine = true,
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenu(
|
|
||||||
expanded = expanded,
|
|
||||||
onDismissRequest = { expanded = false },
|
|
||||||
) {
|
|
||||||
options.forEach { option ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(option) },
|
|
||||||
onClick = {
|
|
||||||
onOptionSelected(option)
|
|
||||||
expanded = false
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun InstallSection(
|
private fun InstallSection(
|
||||||
|
|||||||
Reference in New Issue
Block a user