Skip to content

Commit

Permalink
Merge pull request #585 from zsoltk/fix-drag-vs-align
Browse files Browse the repository at this point in the history
Fix drag vs align
  • Loading branch information
zsoltk authored Sep 9, 2023
2 parents e0f3344 + 901771a commit 6cd22d7
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 134 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- [#579](https://github.com/bumble-tech/appyx/pull/579) – Expose `AndroidLifecycle` in `PlatformLifecycleRegistry` for Android
- [#584](https://github.com/bumble-tech/appyx/pull/584) – Fix applying offset twice in `AppyxComponent`
- [#585](https://github.com/bumble-tech/appyx/pull/585) – Fix drag vs align

## 2.0.0-alpha04

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@ fun DatingCards(modifier: Modifier = Modifier) {
appyxComponent = cards,
gestureValidator = permissiveValidator,
) { elementUiModel ->
Box(
modifier = elementUiModel.modifier
) {
ProfileCard(profile = elementUiModel.element.interactionTarget.profile)
}
ProfileCard(
profile = elementUiModel.element.interactionTarget.profile,
modifier = Modifier.fillMaxSize().then(elementUiModel.modifier)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.boundsInParent
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
Expand All @@ -40,6 +42,10 @@ import com.bumble.appyx.interactions.core.ui.LocalMotionProperties
import com.bumble.appyx.interactions.core.ui.context.TransitionBounds
import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.output.ElementUiModel
import com.bumble.appyx.interactions.core.ui.property.impl.position.PositionInside
import com.bumble.appyx.interactions.core.ui.property.impl.position.PositionInside.Value
import com.bumble.appyx.interactions.core.ui.property.impl.position.PositionOutside
import com.bumble.appyx.interactions.core.ui.property.motionPropertyRenderValue

private val defaultExtraTouch = 48f.dp

Expand Down Expand Up @@ -70,6 +76,7 @@ fun <InteractionTarget : Any, ModelState : Any> DraggableAppyxComponent(
val elementUiModels by appyxComponent.uiModels.collectAsState()
val coroutineScope = rememberCoroutineScope()
val gestureExtraTouchAreaPx = with(density) { gestureExtraTouchArea.toPx() }
var containerSize by remember { mutableStateOf(IntSize.Zero) }
var uiContext by remember { mutableStateOf<UiContext?>(null) }

LaunchedEffect(uiContext) {
Expand All @@ -80,6 +87,7 @@ fun <InteractionTarget : Any, ModelState : Any> DraggableAppyxComponent(
.fillMaxSize()
.then(if (clipToBounds) Modifier.clipToBounds() else Modifier)
.onPlaced {
containerSize = it.size
uiContext = UiContext(
coroutineScope = coroutineScope,
transitionBounds = TransitionBounds(
Expand All @@ -97,80 +105,97 @@ fun <InteractionTarget : Any, ModelState : Any> DraggableAppyxComponent(
appyxComponent.onRelease()
}
}
.fillMaxSize()
) {
CompositionLocalProvider(LocalBoxScope provides this) {
elementUiModels
.forEach { elementUiModel ->
key(elementUiModel.element.id) {
var transformedBoundingBox by remember(elementUiModel.element.id) {
mutableStateOf(Rect.Zero)
}
var size by remember(elementUiModel.element.id) { mutableStateOf(IntSize.Zero) }
var offsetCenter by remember(elementUiModel.element.id) {
mutableStateOf(
Offset.Zero
)
}
val isVisible by elementUiModel.visibleState.collectAsState()
elementUiModel.persistentContainer()
if (isVisible) {
Box(
modifier = Modifier
.offset { offsetCenter.round() }
.width(with(density) { size.width.toDp() })
.height(with(density) { size.height.toDp() })
.pointerInput(appyxComponent) {
detectDragGesturesOrCancellation(
onDragStart = { position ->
appyxComponent.onStartDrag(position)
},
onDrag = { change, dragAmount ->
if (gestureValidator.isGestureValid(
change.position,
transformedBoundingBox.translate(-offsetCenter)
)
) {
change.consume()
appyxComponent.onDrag(dragAmount, density)
true
} else {
elementUiModels.forEach { elementUiModel ->
val id = elementUiModel.element.id

key(id) {
var transformedBoundingBox by remember(id) { mutableStateOf(Rect.Zero) }
var elementSize by remember(id) { mutableStateOf(IntSize.Zero) }
var offsetCenter by remember(id) { mutableStateOf(Offset.Zero) }
val isVisible by elementUiModel.visibleState.collectAsState()

elementUiModel.persistentContainer()

if (isVisible) {
CompositionLocalProvider(
LocalMotionProperties provides elementUiModel.motionProperties
) {
val elementOffset = offsetCenter.round() - elementOffset(elementSize, containerSize)

element.invoke(
elementUiModel.copy(
modifier = Modifier
.offset { elementOffset }
.width(with(density) { elementSize.width.toDp() })
.height(with(density) { elementSize.height.toDp() })
.pointerInput(appyxComponent) {
detectDragGesturesOrCancellation(
onDragStart = { position ->
appyxComponent.onStartDrag(position)
},
onDrag = { change, dragAmount ->
if (gestureValidator.isGestureValid(
change.position,
transformedBoundingBox.translate(-offsetCenter)
)
) {
change.consume()
appyxComponent.onDrag(dragAmount, density)
true
} else {
appyxComponent.onDragEnd()
false
}
},
onDragEnd = {
appyxComponent.onDragEnd()
false
}
},
onDragEnd = {
appyxComponent.onDragEnd()
},
)
}
)
CompositionLocalProvider(
LocalMotionProperties provides elementUiModel.motionProperties
) {
element.invoke(
elementUiModel.copy(
modifier = Modifier
.then(elementUiModel.modifier)
.onPlaced {
size = it.size
val localCenter = Offset(
it.size.width.toFloat(),
it.size.height.toFloat()
) / 2f
},
)
}
.offset { -elementOffset }
.then(elementUiModel.modifier)
.onPlaced {
elementSize = it.size
val localCenter = Offset(
it.size.width.toFloat(),
it.size.height.toFloat()
) / 2f

transformedBoundingBox =
it.boundsInParent()
.inflate(gestureExtraTouchAreaPx)
offsetCenter =
transformedBoundingBox.center - localCenter
}
)
transformedBoundingBox =
it.boundsInParent().inflate(gestureExtraTouchAreaPx)
offsetCenter =
transformedBoundingBox.center - localCenter
}
)
}
)
}
}
}
}
}
}
}


@Composable
fun elementOffset(
elementSize: IntSize,
containerSize: IntSize,
): IntOffset {

val positionInside = motionPropertyRenderValue<Value, PositionInside>()
val positionOutside = motionPropertyRenderValue<PositionOutside.Value, PositionOutside>()
val layoutDirection = LocalLayoutDirection.current

val positionInsideOffset = positionInside?.let {
it.alignment.align(elementSize, containerSize, layoutDirection)
} ?: IntOffset.Zero

val positionOutsideOffset = positionOutside?.let {
it.alignment.align(elementSize, containerSize, layoutDirection)
} ?: IntOffset.Zero

return positionInsideOffset + positionOutsideOffset
}
Loading

0 comments on commit 6cd22d7

Please sign in to comment.