1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-08 12:02:33 +01:00

Add data to draggable

This commit is contained in:
Allan Wang 2023-06-21 22:52:20 -07:00
parent 5567957475
commit a1bf575da1
No known key found for this signature in database
GPG Key ID: C93E3F9C679D7A56
3 changed files with 58 additions and 43 deletions

View File

@ -38,9 +38,9 @@ import com.pitchedapps.frost.ext.toIntOffset
*/
@Composable
fun DragContainer(
fun <T> DragContainer(
modifier: Modifier = Modifier,
draggableState: DraggableState,
draggableState: DraggableState<T>,
content: @Composable () -> Unit
) {
Box(modifier = modifier) {
@ -61,26 +61,28 @@ fun DragContainer(
* dragged.
*/
@Composable
fun DragTarget(
fun <T> DragTarget(
key: String = "",
draggableState: DraggableState,
content: @Composable (isDragging: Boolean) -> Unit
data: T,
draggableState: DraggableState<T>,
content: DraggableComposeContent,
) {
val dragTargetState =
draggableState.rememberDragTarget(
key = key,
data = data,
content = content,
)
Box(
modifier = Modifier.dragTarget(dragTargetState),
) {
content(false)
content(isDragging = false)
}
}
private fun Modifier.dragTarget(dragTargetState: DragTargetState): Modifier {
private fun <T> Modifier.dragTarget(dragTargetState: DragTargetState<T>): Modifier {
return onGloballyPositioned {
dragTargetState.windowPosition = it.positionInWindow()
dragTargetState.size = it.size
@ -111,7 +113,7 @@ private fun Modifier.dragTarget(dragTargetState: DragTargetState): Modifier {
.alpha(if (dragTargetState.isDragging) 0f else 1f)
}
fun Modifier.dropTarget(dropTargetState: DropTargetState): Modifier {
fun <T> Modifier.dropTarget(dropTargetState: DropTargetState<T>): Modifier {
return onGloballyPositioned { dropTargetState.bounds = it.boundsInWindow() }
}
@ -125,14 +127,14 @@ fun Modifier.dropTarget(dropTargetState: DropTargetState): Modifier {
* fillMaxWidth in a grid, but would have a full parent width here without the sizing constraints.
*/
@Composable
private fun DraggingContents(draggableState: DraggableState) {
private fun <T> DraggingContents(draggableState: DraggableState<T>) {
for (target in draggableState.targets) {
DraggingContent(target = target)
}
}
@Composable
private fun DraggingContent(target: DragTargetState) {
private fun <T> DraggingContent(target: DragTargetState<T>) {
val density = LocalDensity.current
Box(
modifier =

View File

@ -28,13 +28,15 @@ import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.unit.IntSize
@Composable
fun rememberDraggableState(): DraggableState {
fun <T> rememberDraggableState(): DraggableState<T> {
return remember { DraggableStateImpl() }
}
interface DraggableState {
typealias DraggableComposeContent = @Composable (isDragging: Boolean) -> Unit
val targets: Collection<DragTargetState>
interface DraggableState<T> {
val targets: Collection<DragTargetState<T>>
/**
* Being drag for target [key].
@ -44,30 +46,27 @@ interface DraggableState {
* Returns true if the request is accepted. It is the caller's responsibility to not propagate
* drag events if the request is denied.
*/
fun onDragStart(key: String, dragTargetState: DragTargetState): Boolean
fun onDragStart(key: String, dragTargetState: DragTargetState<T>): Boolean
fun onDrag(key: String, offset: Offset)
fun onDragEnd(key: String)
@Composable
fun rememberDragTarget(
key: String,
content: @Composable (isDragging: Boolean) -> Unit
): DragTargetState
fun rememberDragTarget(key: String, data: T, content: DraggableComposeContent): DragTargetState<T>
@Composable fun rememberDropTarget(key: String): DropTargetState
@Composable fun rememberDropTarget(key: String): DropTargetState<T>
}
class DraggableStateImpl : DraggableState {
private val activeDragTargets = mutableStateMapOf<String, DragTargetState>()
class DraggableStateImpl<T> : DraggableState<T> {
private val activeDragTargets = mutableStateMapOf<String, DragTargetState<T>>()
private val dropTargets = mutableStateMapOf<String, DropTargetState>()
private val dropTargets = mutableStateMapOf<String, DropTargetState<T>>()
override val targets: Collection<DragTargetState>
override val targets: Collection<DragTargetState<T>>
get() = activeDragTargets.values
override fun onDragStart(key: String, dragTargetState: DragTargetState): Boolean {
override fun onDragStart(key: String, dragTargetState: DragTargetState<T>): Boolean {
if (key in activeDragTargets) return false
activeDragTargets[key] = dragTargetState
return true
@ -93,18 +92,19 @@ class DraggableStateImpl : DraggableState {
@Composable
override fun rememberDragTarget(
key: String,
content: @Composable (isDragging: Boolean) -> Unit
): DragTargetState {
data: T,
content: DraggableComposeContent,
): DragTargetState<T> {
val target =
remember(key, content, this) {
DragTargetState(key = key, draggableState = this, composable = content)
remember(key, data, content, this) {
DragTargetState(key = key, data = data, draggableState = this, composable = content)
}
DisposableEffect(target) { onDispose { activeDragTargets.remove(key) } }
return target
}
@Composable
override fun rememberDropTarget(key: String): DropTargetState {
override fun rememberDropTarget(key: String): DropTargetState<T> {
val target = remember(key, this) { DropTargetState(key, this) }
DisposableEffect(target) {
dropTargets[key] = target
@ -116,11 +116,14 @@ class DraggableStateImpl : DraggableState {
private fun setHover(dragKey: String?, dropKey: String) {
val dropTarget = dropTargets[dropKey] ?: return
dropTarget.hoverKey = dragKey
// Safety check; we only want to register active keys
val dragTarget = if (dragKey != null) activeDragTargets[dragKey] else null
dropTarget.hoverKey = dragTarget?.key
dropTarget.hoverData = dragTarget?.data
}
/** Returns true if drag target exists and is within bounds */
private fun DropTargetState.hasValidDragTarget(): Boolean {
private fun DropTargetState<T>.hasValidDragTarget(): Boolean {
val currentKey = hoverKey ?: return false // no target
val dragTarget = activeDragTargets[currentKey] ?: return false // target not valid
return dragTarget.within(bounds)
@ -152,17 +155,18 @@ class DraggableStateImpl : DraggableState {
}
}
private fun DragTargetState?.within(bounds: Rect): Boolean {
private fun DragTargetState<*>?.within(bounds: Rect): Boolean {
if (this == null) return false
val center = dragPosition + Offset(size.width * 0.5f, size.height * 0.5f)
return bounds.contains(center)
}
/** State for individual dragging target. */
class DragTargetState(
class DragTargetState<T>(
val key: String,
val draggableState: DraggableStateImpl,
val composable: @Composable (isDragging: Boolean) -> Unit
val data: T,
val draggableState: DraggableStateImpl<T>,
val composable: DraggableComposeContent,
) {
var isDragging by mutableStateOf(false)
var windowPosition = Offset.Zero
@ -170,12 +174,18 @@ class DragTargetState(
var size: IntSize by mutableStateOf(IntSize.Zero)
}
class DropTargetState(private val key: String, private val draggableState: DraggableStateImpl) {
class DropTargetState<T>(
private val key: String,
private val draggableState: DraggableStateImpl<T>
) {
var hoverKey: String? by mutableStateOf(null)
var hoverData: String? by mutableStateOf(null)
var hoverData: T? by mutableStateOf(null)
var bounds: Rect = Rect.Zero
set(value) {
field = value
draggableState.checkForDrop(key)
}
val isHovered
get() = hoverKey != null
}

View File

@ -87,7 +87,7 @@ fun TabSelector(
unselected: List<TabData>,
onSelect: (List<TabData>) -> Unit
) {
val draggableState = rememberDraggableState()
val draggableState = rememberDraggableState<TabData>()
DragContainer(modifier = modifier, draggableState = draggableState) {
Column(modifier = Modifier.statusBarsPadding()) {
@ -96,7 +96,7 @@ fun TabSelector(
columns = GridCells.Fixed(4),
) {
items(unselected, key = { it.key }) {
DragTarget(key = it.key, draggableState = draggableState) { isDragging ->
DragTarget(key = it.key, data = it, draggableState = draggableState) { isDragging ->
TabItem(
modifier =
Modifier.thenIf(!isDragging) {
@ -124,7 +124,7 @@ fun TabSelector(
@Composable
fun TabBottomBar(
modifier: Modifier = Modifier,
draggableState: DraggableState,
draggableState: DraggableState<TabData>,
items: List<TabData>
) {
NavigationBar(modifier = modifier) {
@ -133,7 +133,7 @@ fun TabBottomBar(
val alpha by
animateFloatAsState(
targetValue = if (dropTargetState.hoverKey == null) 1f else 0f,
targetValue = if (!dropTargetState.isHovered) 1f else 0.3f,
label = "Nav Item Alpha",
)
@ -141,10 +141,13 @@ fun TabBottomBar(
modifier = Modifier.dropTarget(dropTargetState),
icon = {
// println(dropTargetState.hoverKey)
val iconItem = dropTargetState.hoverData ?: item
Icon(
modifier = Modifier.size(24.dp).alpha(alpha),
imageVector = item.icon,
contentDescription = item.title,
imageVector = iconItem.icon,
contentDescription = iconItem.title,
)
},
selected = false,