fix content shift when the tree is toggled

maxime.batista 1 year ago committed by maxime
parent 2e41bd50d9
commit f035eb854b

@ -1,5 +1,3 @@
import java.net.URI
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)

@ -67,7 +67,7 @@ class MainActivity : ComponentActivity() {
.addConverterFactory(EitherBodyConverter.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(EitherCallAdapterFactory.create())
.baseUrl("http://192.168.127.83:5254/")
.baseUrl("http://grospc:5254/")
.client(
OkHttpClient.Builder()
.addInterceptor { it.proceed(it.request()) }
@ -78,7 +78,7 @@ class MainActivity : ComponentActivity() {
val service = retrofit.create<IQBallService>()
setContent {
IQBallTheme {
IQBallTheme(darkTheme = false) {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
@ -98,5 +98,5 @@ fun App(service: IQBallService) {
auth.getOrNull()!!.token
}
VisualizerPage(service, auth, 3)
VisualizerPage(service, auth, 1)
}

@ -195,8 +195,7 @@ private fun computeSegmentsToPath(
path.moveTo(segmentStart.x, segmentStart.y)
for (i in segments.indices) {
val segment = segments[i]
for ((i, segment) in segments.withIndex()) {
var nextPos = extractPos(segment.next, offsets, area)
if (i == segments.size - 1) {

@ -1,12 +1,24 @@
package com.iqball.app.component
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -14,11 +26,13 @@ import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.boundsInParent
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import arrow.core.getOrNone
import com.iqball.app.R
import com.iqball.app.domains.getPlayerInfo
import com.iqball.app.geo.toVector
@ -50,34 +64,42 @@ fun BasketCourt(
CourtType.Half -> R.drawable.half_court
}
var courtArea by state.courtArea
val zoomState = state.zoomState
Box(
modifier = modifier,
modifier = modifier
.background(Color.LightGray)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.width(IntrinsicSize.Min)
.zoomable(zoomState),
contentAlignment = Alignment.Center,
) {
Image(
painter = painterResource(id = courtImg),
contentDescription = "court",
modifier = Modifier
.background(Color.White)
.onGloballyPositioned {
if (courtArea == Rect.Zero)
courtArea = it.boundsInRoot()
}
)
CourtContent(
courtAreaState = state.courtArea,
courtArea = courtArea,
content = content,
offsets = state.stepComponentsOffsets,
isFromParent = false
)
if (parentContent != null) {
CourtContent(
courtAreaState = state.courtArea,
courtArea = courtArea,
content = parentContent,
offsets = state.parentComponentsOffsets,
isFromParent = true
@ -89,26 +111,31 @@ fun BasketCourt(
@Composable
private fun CourtContent(
courtAreaState: MutableState<Rect>,
courtArea: Rect,
content: StepContent,
offsets: MutableMap<ComponentId, Offset>,
isFromParent: Boolean
) {
var courtArea by courtAreaState
val playersPixelsRadius = LocalDensity.current.run { PlayerPieceDiameterDp.dp.toPx() / 2 }
val width = LocalDensity.current.run { courtArea.width.toDp() }
val height = LocalDensity.current.run { courtArea.height.toDp() }
Box(
modifier = Modifier
.fillMaxSize()
.onGloballyPositioned {
if (courtArea == Rect.Zero)
courtArea = it.boundsInRoot()
}
.requiredWidth(width)
.requiredHeight(height)
.drawWithContent {
val relativeOffsets =
offsets.mapValues { (it.value - courtArea.topLeft).toVector() }
drawActions(this, content, relativeOffsets, courtArea, playersPixelsRadius, if (isFromParent) Color.Gray else Color.Black)
offsets.mapValues { (it.value).toVector() }
drawActions(
this,
content,
relativeOffsets,
courtArea,
playersPixelsRadius,
if (isFromParent) Color.Gray else Color.Black
)
drawContent()
}
) {
@ -117,8 +144,8 @@ private fun CourtContent(
for (component in content.components) {
val componentModifier = Modifier
.onGloballyPositioned {
if (!offsets.containsKey(component.id))
offsets[component.id] = it.boundsInRoot().center
if (!offsets.getOrNone(component.id).isSome { it != Offset.Zero })
offsets[component.id] = it.boundsInParent().center
}
when (component) {
is PlayerLike -> {

@ -14,7 +14,7 @@ import com.iqball.app.model.tactic.StepComponent
import com.iqball.app.model.tactic.StepContent
/**
* Converts the phantom's [PhantomPositioning] to a XY Position
* Converts the phantom's [Positioning] to a XY Position
* if the phantom is a [RelativePositioning], the XY coords are determined
* using the attached component, and by expecting that there is an action on the attached component that
* targets the given phantom.

@ -105,16 +105,18 @@ fun VisualizerPage(
onNodeSelected = { selectedStepId = it.id })
Configuration.ORIENTATION_LANDSCAPE -> {
val courtArea = remember { mutableStateOf(Rect.Zero) }
val stepOffsets =
remember(showTree.value, content) { mutableStateMapOf<ComponentId, Offset>() }
remember(selectedStepId) { mutableStateMapOf<ComponentId, Offset>() }
val parentOffsets =
remember(showTree.value, parentContent) { mutableStateMapOf<ComponentId, Offset>() }
val courtArea = remember(showTree.value) { mutableStateOf(Rect.Zero) }
val courtZoomState = remember(showTree.value, selectedStepId) { ZoomState() }
remember(selectedStepId) { mutableStateMapOf<ComponentId, Offset>() }
val courtModifier =
if (showTree.value) Modifier.width(IntrinsicSize.Min) else Modifier.fillMaxWidth()
val courtZoomState = remember { ZoomState() }
Row(modifier = Modifier.background(Color.LightGray)) {
BasketCourt(
content = content,

@ -28,7 +28,10 @@ private class EitherTypeAdapter(
JsonReader.Token.STRING -> JsonPrimitiveType.String
JsonReader.Token.NUMBER -> JsonPrimitiveType.Number
JsonReader.Token.BOOLEAN -> JsonPrimitiveType.Boolean
JsonReader.Token.NULL -> return null
JsonReader.Token.NULL -> {
reader.nextNull<Any>()
return null
}
else -> throw JsonDataException("unexpected token : $token")
}

Loading…
Cancel
Save