mirror of
https://github.com/AllanWang/Frost-for-Facebook.git
synced 2024-11-08 20:12:39 +01:00
Add data to draggable
This commit is contained in:
parent
5567957475
commit
a1bf575da1
@ -38,9 +38,9 @@ import com.pitchedapps.frost.ext.toIntOffset
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DragContainer(
|
fun <T> DragContainer(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
draggableState: DraggableState,
|
draggableState: DraggableState<T>,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
@ -61,26 +61,28 @@ fun DragContainer(
|
|||||||
* dragged.
|
* dragged.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun DragTarget(
|
fun <T> DragTarget(
|
||||||
key: String = "",
|
key: String = "",
|
||||||
draggableState: DraggableState,
|
data: T,
|
||||||
content: @Composable (isDragging: Boolean) -> Unit
|
draggableState: DraggableState<T>,
|
||||||
|
content: DraggableComposeContent,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val dragTargetState =
|
val dragTargetState =
|
||||||
draggableState.rememberDragTarget(
|
draggableState.rememberDragTarget(
|
||||||
key = key,
|
key = key,
|
||||||
|
data = data,
|
||||||
content = content,
|
content = content,
|
||||||
)
|
)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.dragTarget(dragTargetState),
|
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 {
|
return onGloballyPositioned {
|
||||||
dragTargetState.windowPosition = it.positionInWindow()
|
dragTargetState.windowPosition = it.positionInWindow()
|
||||||
dragTargetState.size = it.size
|
dragTargetState.size = it.size
|
||||||
@ -111,7 +113,7 @@ private fun Modifier.dragTarget(dragTargetState: DragTargetState): Modifier {
|
|||||||
.alpha(if (dragTargetState.isDragging) 0f else 1f)
|
.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() }
|
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.
|
* fillMaxWidth in a grid, but would have a full parent width here without the sizing constraints.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun DraggingContents(draggableState: DraggableState) {
|
private fun <T> DraggingContents(draggableState: DraggableState<T>) {
|
||||||
for (target in draggableState.targets) {
|
for (target in draggableState.targets) {
|
||||||
DraggingContent(target = target)
|
DraggingContent(target = target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DraggingContent(target: DragTargetState) {
|
private fun <T> DraggingContent(target: DragTargetState<T>) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
|
@ -28,13 +28,15 @@ import androidx.compose.ui.geometry.Rect
|
|||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberDraggableState(): DraggableState {
|
fun <T> rememberDraggableState(): DraggableState<T> {
|
||||||
return remember { DraggableStateImpl() }
|
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].
|
* 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
|
* Returns true if the request is accepted. It is the caller's responsibility to not propagate
|
||||||
* drag events if the request is denied.
|
* 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 onDrag(key: String, offset: Offset)
|
||||||
|
|
||||||
fun onDragEnd(key: String)
|
fun onDragEnd(key: String)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberDragTarget(
|
fun rememberDragTarget(key: String, data: T, content: DraggableComposeContent): DragTargetState<T>
|
||||||
key: String,
|
|
||||||
content: @Composable (isDragging: Boolean) -> Unit
|
|
||||||
): DragTargetState
|
|
||||||
|
|
||||||
@Composable fun rememberDropTarget(key: String): DropTargetState
|
@Composable fun rememberDropTarget(key: String): DropTargetState<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
class DraggableStateImpl : DraggableState {
|
class DraggableStateImpl<T> : DraggableState<T> {
|
||||||
private val activeDragTargets = mutableStateMapOf<String, DragTargetState>()
|
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
|
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
|
if (key in activeDragTargets) return false
|
||||||
activeDragTargets[key] = dragTargetState
|
activeDragTargets[key] = dragTargetState
|
||||||
return true
|
return true
|
||||||
@ -93,18 +92,19 @@ class DraggableStateImpl : DraggableState {
|
|||||||
@Composable
|
@Composable
|
||||||
override fun rememberDragTarget(
|
override fun rememberDragTarget(
|
||||||
key: String,
|
key: String,
|
||||||
content: @Composable (isDragging: Boolean) -> Unit
|
data: T,
|
||||||
): DragTargetState {
|
content: DraggableComposeContent,
|
||||||
|
): DragTargetState<T> {
|
||||||
val target =
|
val target =
|
||||||
remember(key, content, this) {
|
remember(key, data, content, this) {
|
||||||
DragTargetState(key = key, draggableState = this, composable = content)
|
DragTargetState(key = key, data = data, draggableState = this, composable = content)
|
||||||
}
|
}
|
||||||
DisposableEffect(target) { onDispose { activeDragTargets.remove(key) } }
|
DisposableEffect(target) { onDispose { activeDragTargets.remove(key) } }
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun rememberDropTarget(key: String): DropTargetState {
|
override fun rememberDropTarget(key: String): DropTargetState<T> {
|
||||||
val target = remember(key, this) { DropTargetState(key, this) }
|
val target = remember(key, this) { DropTargetState(key, this) }
|
||||||
DisposableEffect(target) {
|
DisposableEffect(target) {
|
||||||
dropTargets[key] = target
|
dropTargets[key] = target
|
||||||
@ -116,11 +116,14 @@ class DraggableStateImpl : DraggableState {
|
|||||||
|
|
||||||
private fun setHover(dragKey: String?, dropKey: String) {
|
private fun setHover(dragKey: String?, dropKey: String) {
|
||||||
val dropTarget = dropTargets[dropKey] ?: return
|
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 */
|
/** 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 currentKey = hoverKey ?: return false // no target
|
||||||
val dragTarget = activeDragTargets[currentKey] ?: return false // target not valid
|
val dragTarget = activeDragTargets[currentKey] ?: return false // target not valid
|
||||||
return dragTarget.within(bounds)
|
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
|
if (this == null) return false
|
||||||
val center = dragPosition + Offset(size.width * 0.5f, size.height * 0.5f)
|
val center = dragPosition + Offset(size.width * 0.5f, size.height * 0.5f)
|
||||||
return bounds.contains(center)
|
return bounds.contains(center)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** State for individual dragging target. */
|
/** State for individual dragging target. */
|
||||||
class DragTargetState(
|
class DragTargetState<T>(
|
||||||
val key: String,
|
val key: String,
|
||||||
val draggableState: DraggableStateImpl,
|
val data: T,
|
||||||
val composable: @Composable (isDragging: Boolean) -> Unit
|
val draggableState: DraggableStateImpl<T>,
|
||||||
|
val composable: DraggableComposeContent,
|
||||||
) {
|
) {
|
||||||
var isDragging by mutableStateOf(false)
|
var isDragging by mutableStateOf(false)
|
||||||
var windowPosition = Offset.Zero
|
var windowPosition = Offset.Zero
|
||||||
@ -170,12 +174,18 @@ class DragTargetState(
|
|||||||
var size: IntSize by mutableStateOf(IntSize.Zero)
|
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 hoverKey: String? by mutableStateOf(null)
|
||||||
var hoverData: String? by mutableStateOf(null)
|
var hoverData: T? by mutableStateOf(null)
|
||||||
var bounds: Rect = Rect.Zero
|
var bounds: Rect = Rect.Zero
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
draggableState.checkForDrop(key)
|
draggableState.checkForDrop(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isHovered
|
||||||
|
get() = hoverKey != null
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ fun TabSelector(
|
|||||||
unselected: List<TabData>,
|
unselected: List<TabData>,
|
||||||
onSelect: (List<TabData>) -> Unit
|
onSelect: (List<TabData>) -> Unit
|
||||||
) {
|
) {
|
||||||
val draggableState = rememberDraggableState()
|
val draggableState = rememberDraggableState<TabData>()
|
||||||
|
|
||||||
DragContainer(modifier = modifier, draggableState = draggableState) {
|
DragContainer(modifier = modifier, draggableState = draggableState) {
|
||||||
Column(modifier = Modifier.statusBarsPadding()) {
|
Column(modifier = Modifier.statusBarsPadding()) {
|
||||||
@ -96,7 +96,7 @@ fun TabSelector(
|
|||||||
columns = GridCells.Fixed(4),
|
columns = GridCells.Fixed(4),
|
||||||
) {
|
) {
|
||||||
items(unselected, key = { it.key }) {
|
items(unselected, key = { it.key }) {
|
||||||
DragTarget(key = it.key, draggableState = draggableState) { isDragging ->
|
DragTarget(key = it.key, data = it, draggableState = draggableState) { isDragging ->
|
||||||
TabItem(
|
TabItem(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.thenIf(!isDragging) {
|
Modifier.thenIf(!isDragging) {
|
||||||
@ -124,7 +124,7 @@ fun TabSelector(
|
|||||||
@Composable
|
@Composable
|
||||||
fun TabBottomBar(
|
fun TabBottomBar(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
draggableState: DraggableState,
|
draggableState: DraggableState<TabData>,
|
||||||
items: List<TabData>
|
items: List<TabData>
|
||||||
) {
|
) {
|
||||||
NavigationBar(modifier = modifier) {
|
NavigationBar(modifier = modifier) {
|
||||||
@ -133,7 +133,7 @@ fun TabBottomBar(
|
|||||||
|
|
||||||
val alpha by
|
val alpha by
|
||||||
animateFloatAsState(
|
animateFloatAsState(
|
||||||
targetValue = if (dropTargetState.hoverKey == null) 1f else 0f,
|
targetValue = if (!dropTargetState.isHovered) 1f else 0.3f,
|
||||||
label = "Nav Item Alpha",
|
label = "Nav Item Alpha",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -141,10 +141,13 @@ fun TabBottomBar(
|
|||||||
modifier = Modifier.dropTarget(dropTargetState),
|
modifier = Modifier.dropTarget(dropTargetState),
|
||||||
icon = {
|
icon = {
|
||||||
// println(dropTargetState.hoverKey)
|
// println(dropTargetState.hoverKey)
|
||||||
|
|
||||||
|
val iconItem = dropTargetState.hoverData ?: item
|
||||||
|
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.size(24.dp).alpha(alpha),
|
modifier = Modifier.size(24.dp).alpha(alpha),
|
||||||
imageVector = item.icon,
|
imageVector = iconItem.icon,
|
||||||
contentDescription = item.title,
|
contentDescription = iconItem.title,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
selected = false,
|
selected = false,
|
||||||
|
Loading…
Reference in New Issue
Block a user