1
1
package com.flowintent.workspace.ui
2
2
3
+ import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
3
4
import androidx.compose.foundation.layout.Arrangement
5
+ import androidx.compose.foundation.layout.Box
4
6
import androidx.compose.foundation.layout.Column
5
7
import androidx.compose.foundation.layout.PaddingValues
6
8
import androidx.compose.foundation.layout.Row
7
9
import androidx.compose.foundation.layout.fillMaxSize
8
10
import androidx.compose.foundation.layout.fillMaxWidth
9
11
import androidx.compose.foundation.layout.padding
10
12
import androidx.compose.foundation.lazy.LazyColumn
11
- import androidx.compose.foundation.lazy.items
13
+ import androidx.compose.foundation.lazy.itemsIndexed
12
14
import androidx.compose.material.icons.Icons
13
15
import androidx.compose.material.icons.automirrored.filled.Label
14
16
import androidx.compose.material.icons.filled.Category
@@ -20,16 +22,27 @@ import androidx.compose.material3.IconButton
20
22
import androidx.compose.material3.Text
21
23
import androidx.compose.runtime.Composable
22
24
import androidx.compose.runtime.getValue
25
+ import androidx.compose.runtime.mutableStateOf
26
+ import androidx.compose.runtime.remember
27
+ import androidx.compose.runtime.setValue
28
+ import androidx.compose.runtime.toMutableStateList
23
29
import androidx.compose.ui.Alignment
24
30
import androidx.compose.ui.Modifier
25
31
import androidx.compose.ui.graphics.RectangleShape
32
+ import androidx.compose.ui.graphics.graphicsLayer
33
+ import androidx.compose.ui.input.pointer.pointerInput
26
34
import androidx.compose.ui.text.font.FontFamily
27
35
import androidx.compose.ui.text.font.FontWeight
28
36
import androidx.compose.ui.tooling.preview.Preview
29
37
import androidx.compose.ui.unit.dp
30
38
import androidx.compose.ui.unit.sp
39
+ import androidx.compose.ui.zIndex
31
40
import androidx.hilt.navigation.compose.hiltViewModel
32
41
import androidx.lifecycle.compose.collectAsStateWithLifecycle
42
+ import com.flowintent.core.db.DragInfo
43
+ import com.flowintent.core.db.Task
44
+ import com.flowintent.core.db.calculateNewIndex
45
+ import com.flowintent.core.db.swap
33
46
import com.flowintent.workspace.nav.ToDoNavTopBar
34
47
import com.flowintent.workspace.ui.vm.TaskViewModel
35
48
import com.flowintent.workspace.util.asString
@@ -101,36 +114,92 @@ fun ListActionBar(paddingTopOffset: PaddingValues) {
101
114
@Composable
102
115
private fun ListCardContent (viewModel : TaskViewModel = hiltViewModel()) {
103
116
val taskList by viewModel.tasks.collectAsStateWithLifecycle()
117
+ val list = remember(taskList) { taskList.toMutableStateList() }
118
+ var draggingItem by remember { mutableStateOf<DragInfo ?>(null ) }
119
+ var itemHeight by remember { mutableStateOf(50 .dp) }
120
+
104
121
LazyColumn (
105
- modifier = Modifier
106
- .fillMaxSize(),
122
+ modifier = Modifier .fillMaxSize(),
107
123
horizontalAlignment = Alignment .CenterHorizontally
108
124
) {
109
- items(taskList) { task ->
110
- SwipeableCard (
111
- task = task,
112
- onDelete = { viewModel.deleteTask(task) },
113
- onEdit = { viewModel.setUpdateTaskId(task.uid) },
114
- viewModel = viewModel
125
+ itemsIndexed(list, key = { index: Int , task: Task -> task.uid }) { index, task ->
126
+ val isDragging = draggingItem?.index == index
127
+
128
+ Box (
129
+ modifier = Modifier
130
+ .graphicsLayer {
131
+ if (isDragging) {
132
+ val heightPx = itemHeight.toPx()
133
+ translationY = draggingItem?.let { drag ->
134
+ drag.offsetY - drag.touchOffset + heightPx / 2f
135
+ } ? : 0f
136
+ shadowElevation = 16 .dp.toPx()
137
+ scaleX = 1.02f
138
+ scaleY = 1.02f
139
+ }
140
+ }
141
+ .zIndex(if (isDragging) 1f else 0f )
142
+ .pointerInput(Unit ) {
143
+ detectDragGesturesAfterLongPress(
144
+ onDragStart = { offset ->
145
+ draggingItem = DragInfo (
146
+ index = index,
147
+ offsetY = 0f ,
148
+ touchOffset = offset.y
149
+ )
150
+ },
151
+ onDrag = { change, dragAmount ->
152
+ change.consume()
153
+ val drag = draggingItem ? : return @detectDragGesturesAfterLongPress
154
+
155
+ draggingItem = drag.copy(
156
+ offsetY = drag.offsetY + dragAmount.y
157
+ )
158
+
159
+ val newIndex = calculateNewIndex(
160
+ draggingItem!! ,
161
+ list.size,
162
+ itemHeight = itemHeight.toPx()
163
+ )
164
+ if (newIndex != draggingItem!! .index) {
165
+ list.swap(draggingItem!! .index, newIndex)
166
+ draggingItem = draggingItem!! .copy(
167
+ index = newIndex,
168
+ offsetY = 0f
169
+ )
170
+ }
171
+ },
172
+ onDragEnd = { draggingItem = null },
173
+ onDragCancel = { draggingItem = null }
174
+ )
175
+ }
115
176
) {
116
- Column (
117
- modifier = Modifier
118
- .fillMaxWidth()
119
- .padding(12 .dp)
177
+ SwipeableCard (
178
+ task = task,
179
+ onDelete = { viewModel.deleteTask(task) },
180
+ onEdit = { viewModel.setUpdateTaskId(task.uid) },
181
+ onHeightChange = { itemHeight = it },
182
+ modifier = Modifier .padding(start = 12 .dp, top = 12 .dp, end = 12 .dp)
120
183
) {
121
- Text (
122
- text = task.title,
123
- fontSize = 16 .sp,
124
- fontFamily = FontFamily .SansSerif ,
125
- fontWeight = FontWeight .Bold
126
- )
127
- Text (
128
- text = task.content.asString(),
129
- modifier = Modifier .padding(top = 12 .dp),
130
- fontSize = 16 .sp,
131
- fontFamily = FontFamily .SansSerif ,
132
- fontWeight = FontWeight .Bold
133
- )
184
+ Column (
185
+ modifier = Modifier
186
+ .fillMaxWidth()
187
+ .padding(12 .dp)
188
+ ) {
189
+ Text (
190
+ text = task.title,
191
+ fontSize = 16 .sp,
192
+ fontFamily = FontFamily .SansSerif ,
193
+ fontWeight = FontWeight .Bold
194
+ )
195
+ Text (
196
+ text = task.content.asString(),
197
+ modifier = Modifier .padding(top = 12 .dp),
198
+ fontSize = 16 .sp,
199
+ fontFamily = FontFamily .SansSerif ,
200
+ fontWeight = FontWeight .Bold
201
+ )
202
+ }
134
203
}
135
204
}
136
205
}
0 commit comments