Skip to content

Commit

Permalink
Merge pull request #542 from manuel-martos/desktop-widget-representation
Browse files Browse the repository at this point in the history
Desktop widget representation
  • Loading branch information
mmartosdev authored Jul 26, 2023
2 parents 9ced2ae + 80ff111 commit ec1952d
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 2 deletions.
5 changes: 5 additions & 0 deletions demos/appyx-interactions/desktop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
id("com.google.devtools.ksp")
}

kotlin {
Expand Down Expand Up @@ -45,3 +46,7 @@ compose.desktop {
}
}
}

dependencies {
add("kspDesktop", project(":ksp:mutable-ui-processor"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import com.bumble.appyx.components.spotlight.SpotlightModel
import com.bumble.appyx.components.spotlight.operation.next
import com.bumble.appyx.components.spotlight.operation.previous
import com.bumble.appyx.components.spotlight.ui.slider.SpotlightSlider
import com.bumble.appyx.components.spotlight.ui.stack3d.SpotlightStack3D
import com.bumble.appyx.interactions.Events
import com.bumble.appyx.interactions.core.DraggableAppyxComponent
import com.bumble.appyx.interactions.core.ui.gesture.GestureSettleConfig
import com.bumble.appyx.interactions.core.ui.helper.AppyxComponentSetup
import com.bumble.appyx.interactions.core.ui.output.ElementUiModel
import com.bumble.appyx.interactions.widgets.ui.WidgetsStack3D
import com.bumble.appyx.samples.common.widget.CalendarWidget
import com.bumble.appyx.samples.common.widget.TimerWidget
import com.bumble.appyx.samples.common.widget.WeatherWidget
Expand Down Expand Up @@ -50,7 +50,7 @@ fun Widgets(
items = items,
savedStateMap = null
),
motionController = { SpotlightStack3D(it) },
motionController = { WidgetsStack3D(it) },
animationSpec = spring(stiffness = Spring.StiffnessVeryLow / 4),
gestureFactory = {
SpotlightSlider.Gestures(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.bumble.appyx.interactions.widgets.ui

import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.math.clamp
import com.bumble.appyx.interactions.core.ui.math.smoothstep
import com.bumble.appyx.interactions.core.ui.property.impl.Alpha
import com.bumble.appyx.interactions.core.ui.property.impl.Position
import com.bumble.appyx.interactions.core.ui.property.impl.RotationX
import com.bumble.appyx.interactions.core.ui.property.impl.Scale
import com.bumble.appyx.interactions.core.ui.property.impl.ZIndex
import com.bumble.appyx.interactions.core.ui.state.MutableUiStateSpecs
import com.bumble.appyx.mapState
import kotlinx.coroutines.flow.StateFlow

@MutableUiStateSpecs
class TargetUiState(
val positionInList: Int = 0,
val rotationX: RotationX.Target,
val position: Position.Target,
val scale: Scale.Target,
val alpha: Alpha.Target,
val zIndex: ZIndex.Target,
) {
constructor(
base: TargetUiState,
positionInList: Int,
) : this(
positionInList = positionInList,
rotationX = base.rotationX,
position = base.position,
scale = base.scale,
alpha = base.alpha,
zIndex = base.zIndex,
)

fun toMutableState(
uiContext: UiContext,
scrollX: StateFlow<Float>,
itemWidth: Dp,
itemHeight: Dp,
itemsInStack: Int = 3,
): MutableUiState {
return MutableUiState(
uiContext = uiContext,
rotationX = RotationX(
uiContext = uiContext,
target = rotationX,
displacement = scrollX.mapState(uiContext.coroutineScope) {
2.5f * clamp(-it, 0f, 1f)
},
origin = TransformOrigin(0.075f, 0f),
),
position = Position(
uiContext = uiContext,
target = position,
displacement = scrollX.mapState(uiContext.coroutineScope) {
val factor = 0.075f + smoothstep(0f, 1f, it)
Position.Value(
offset = DpOffset(
x = (-0.125f * it * itemWidth.value).dp,
y = (-factor * it * itemHeight.value / (1f - 0.1f * it)).dp,
)
)
}
),
scale = Scale(
uiContext = uiContext,
target = scale,
displacement = scrollX.mapState(uiContext.coroutineScope) { -0.1f * it },
),
alpha = Alpha(
uiContext = uiContext,
target = alpha,
displacement = scrollX.mapState(uiContext.coroutineScope) {
clamp(it, 0f, 1f) + clamp(-it - itemsInStack, 0f, 1f)
},
),
zIndex = ZIndex(
uiContext = uiContext,
target = zIndex,
displacement = scrollX.mapState(uiContext.coroutineScope) { -it }
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.bumble.appyx.interactions.widgets.ui

import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import com.bumble.appyx.components.spotlight.SpotlightModel
import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.property.impl.Alpha
import com.bumble.appyx.interactions.core.ui.property.impl.GenericFloatProperty
import com.bumble.appyx.interactions.core.ui.property.impl.Position
import com.bumble.appyx.interactions.core.ui.property.impl.RotationX
import com.bumble.appyx.interactions.core.ui.property.impl.Scale
import com.bumble.appyx.interactions.core.ui.property.impl.ZIndex
import com.bumble.appyx.interactions.core.ui.state.MatchedTargetUiState
import com.bumble.appyx.mapState
import com.bumble.appyx.transitionmodel.BaseMotionController

class WidgetsStack3D<InteractionTarget : Any>(
uiContext: UiContext,
) : BaseMotionController<InteractionTarget, SpotlightModel.State<InteractionTarget>, MutableUiState, TargetUiState>(
uiContext = uiContext,
) {
private val width: Dp = uiContext.transitionBounds.widthDp
private val height: Dp = uiContext.transitionBounds.heightDp

private val scrollY = GenericFloatProperty(uiContext, GenericFloatProperty.Target(0f))
override val viewpointDimensions: List<Pair<(SpotlightModel.State<InteractionTarget>) -> Float, GenericFloatProperty>> =
listOf(
{ state: SpotlightModel.State<InteractionTarget> -> state.activeIndex } to scrollY
)

override fun SpotlightModel.State<InteractionTarget>.toUiTargets(): List<MatchedTargetUiState<InteractionTarget, TargetUiState>> =
positions.flatMapIndexed { index, position ->
position.elements.map {
MatchedTargetUiState(
element = it.key,
targetUiState = TargetUiState(
base = default,
positionInList = index,
)
)
}
}

override fun mutableUiStateFor(
uiContext: UiContext,
targetUiState: TargetUiState
): MutableUiState =
targetUiState.toMutableState(
uiContext = uiContext,
scrollX = scrollY.renderValueFlow.mapState(uiContext.coroutineScope) { it - targetUiState.positionInList },
itemWidth = width,
itemHeight = height,
)

private companion object {
val default: TargetUiState = TargetUiState(
rotationX = RotationX.Target(0f),
position = Position.Target(DpOffset.Zero),
scale = Scale.Target(1f),
alpha = Alpha.Target(1f),
zIndex = ZIndex.Target(0f),
)
}
}

0 comments on commit ec1952d

Please sign in to comment.