Skip to content

Commit fdca598

Browse files
committed
app: drag to reorder upstream interfaces in container config
Selected upstream interfaces could previously only be added or removed, not reordered, even though order matters (first entry is the primary upstream, the rest are fallbacks). Reuses the same drag-to-reorder pattern as the Auto Boot Priority screen: a LazyColumn driven by sh.calvin.reorderable's live onMove callback, so the reordered list propagates immediately as items cross during the drag rather than only after a settle animation. Signed-off-by: ravindu644 <droidcasts@protonmail.com>
1 parent 6f31e04 commit fdca598

1 file changed

Lines changed: 42 additions & 16 deletions

File tree

‎Android/app/src/main/java/com/droidspaces/app/ui/component/UpstreamInterfaceList.kt‎

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@ import androidx.compose.animation.core.LinearEasing
44
import androidx.compose.animation.core.animateFloatAsState
55
import androidx.compose.animation.core.tween
66
import androidx.compose.foundation.BorderStroke
7+
import androidx.compose.foundation.ExperimentalFoundationApi
78
import androidx.compose.foundation.clickable
89
import androidx.compose.foundation.layout.*
10+
import androidx.compose.foundation.lazy.LazyColumn
11+
import androidx.compose.foundation.lazy.items
12+
import androidx.compose.foundation.lazy.rememberLazyListState
913
import androidx.compose.foundation.rememberScrollState
1014
import androidx.compose.foundation.shape.RoundedCornerShape
1115
import androidx.compose.foundation.verticalScroll
1216
import androidx.compose.material.icons.Icons
1317
import androidx.compose.material.icons.filled.Add
1418
import androidx.compose.material.icons.filled.Delete
19+
import androidx.compose.material.icons.filled.DragHandle
1520
import androidx.compose.material.icons.filled.Refresh
1621
import androidx.compose.material3.*
1722
import androidx.compose.runtime.*
@@ -28,8 +33,10 @@ import com.droidspaces.app.R
2833
import com.droidspaces.app.util.ContainerManager
2934
import kotlinx.coroutines.delay
3035
import kotlinx.coroutines.launch
36+
import sh.calvin.reorderable.ReorderableItem
37+
import sh.calvin.reorderable.rememberReorderableLazyListState
3138

32-
@OptIn(ExperimentalLayoutApi::class)
39+
@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class)
3340
@Composable
3441
fun UpstreamInterfaceList(
3542
upstreamInterfaces: List<String>,
@@ -45,21 +52,40 @@ fun UpstreamInterfaceList(
4552
}
4653

4754
Column(modifier = modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
48-
// Selected interfaces
49-
upstreamInterfaces.forEach { iface ->
50-
Surface(
51-
modifier = Modifier.fillMaxWidth(),
52-
color = MaterialTheme.colorScheme.surfaceContainerHigh,
53-
shape = RoundedCornerShape(20.dp),
54-
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
55-
) {
56-
Row(
57-
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
58-
verticalAlignment = Alignment.CenterVertically
59-
) {
60-
Text(text = iface, modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyLarge)
61-
IconButton(onClick = { onInterfacesChange(upstreamInterfaces - iface) }) {
62-
Icon(Icons.Default.Delete, contentDescription = null, tint = MaterialTheme.colorScheme.error)
55+
// Selected interfaces (drag the handle to reorder; height is bounded since
56+
// this LazyColumn sits inside the form's own scrolling Column)
57+
val lazyListState = rememberLazyListState()
58+
val reorderableState = rememberReorderableLazyListState(lazyListState) { from, to ->
59+
onInterfacesChange(upstreamInterfaces.toMutableList().apply { add(to.index, removeAt(from.index)) })
60+
}
61+
LazyColumn(
62+
state = lazyListState,
63+
modifier = Modifier.fillMaxWidth().heightIn(max = 480.dp),
64+
verticalArrangement = Arrangement.spacedBy(12.dp)
65+
) {
66+
items(upstreamInterfaces, key = { it }) { iface ->
67+
ReorderableItem(reorderableState, key = iface) {
68+
Surface(
69+
modifier = Modifier.fillMaxWidth(),
70+
color = MaterialTheme.colorScheme.surfaceContainerHigh,
71+
shape = RoundedCornerShape(20.dp),
72+
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
73+
) {
74+
Row(
75+
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
76+
verticalAlignment = Alignment.CenterVertically
77+
) {
78+
Icon(
79+
Icons.Default.DragHandle,
80+
contentDescription = null,
81+
modifier = Modifier.draggableHandle().padding(end = 12.dp),
82+
tint = MaterialTheme.colorScheme.onSurfaceVariant
83+
)
84+
Text(text = iface, modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyLarge)
85+
IconButton(onClick = { onInterfacesChange(upstreamInterfaces - iface) }) {
86+
Icon(Icons.Default.Delete, contentDescription = null, tint = MaterialTheme.colorScheme.error)
87+
}
88+
}
6389
}
6490
}
6591
}

0 commit comments

Comments
 (0)