From 3bd501af3a454bc9c30547da3987a338a72a1bb8 Mon Sep 17 00:00:00 2001 From: "maxime.batista" Date: Thu, 28 Mar 2024 12:05:46 +0100 Subject: [PATCH] fix content shift when the tree is toggled --- app/build.gradle.kts | 2 - .../main/java/com/iqball/app/MainActivity.kt | 4 +- .../java/com/iqball/app/component/Actions.kt | 3 +- .../com/iqball/app/component/BasketCourt.kt | 59 ++++++++++++++----- .../com/iqball/app/domains/PlayerDomains.kt | 2 +- .../com/iqball/app/page/VisualizerPage.kt | 23 +++++--- .../app/serialization/EitherTypeAdapter.kt | 5 +- 7 files changed, 67 insertions(+), 31 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c895a00..69fd629 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,5 +1,3 @@ -import java.net.URI - plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) diff --git a/app/src/main/java/com/iqball/app/MainActivity.kt b/app/src/main/java/com/iqball/app/MainActivity.kt index 0ccbd39..2bab3f4 100644 --- a/app/src/main/java/com/iqball/app/MainActivity.kt +++ b/app/src/main/java/com/iqball/app/MainActivity.kt @@ -74,7 +74,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()) } @@ -85,7 +85,7 @@ class MainActivity : ComponentActivity() { val service = retrofit.create() setContent { - IQBallTheme { + IQBallTheme(darkTheme = false) { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), diff --git a/app/src/main/java/com/iqball/app/component/Actions.kt b/app/src/main/java/com/iqball/app/component/Actions.kt index 6460dc0..cff94ee 100644 --- a/app/src/main/java/com/iqball/app/component/Actions.kt +++ b/app/src/main/java/com/iqball/app/component/Actions.kt @@ -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) { diff --git a/app/src/main/java/com/iqball/app/component/BasketCourt.kt b/app/src/main/java/com/iqball/app/component/BasketCourt.kt index f6876cf..a5ad403 100644 --- a/app/src/main/java/com/iqball/app/component/BasketCourt.kt +++ b/app/src/main/java/com/iqball/app/component/BasketCourt.kt @@ -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, + courtArea: Rect, content: StepContent, offsets: MutableMap, 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 -> { diff --git a/app/src/main/java/com/iqball/app/domains/PlayerDomains.kt b/app/src/main/java/com/iqball/app/domains/PlayerDomains.kt index c084757..df0fe7d 100644 --- a/app/src/main/java/com/iqball/app/domains/PlayerDomains.kt +++ b/app/src/main/java/com/iqball/app/domains/PlayerDomains.kt @@ -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. diff --git a/app/src/main/java/com/iqball/app/page/VisualizerPage.kt b/app/src/main/java/com/iqball/app/page/VisualizerPage.kt index 2159878..bb8f602 100644 --- a/app/src/main/java/com/iqball/app/page/VisualizerPage.kt +++ b/app/src/main/java/com/iqball/app/page/VisualizerPage.kt @@ -105,28 +105,37 @@ fun VisualizerPage( onNodeSelected = { selectedStepId = it.id }) Configuration.ORIENTATION_LANDSCAPE -> { + val courtArea = remember { mutableStateOf(Rect.Zero) } val stepOffsets = - remember(showTree.value, content) { mutableStateMapOf() } + remember(selectedStepId) { mutableStateMapOf() } val parentOffsets = - remember(showTree.value, parentContent) { mutableStateMapOf() } - val courtArea = remember(showTree.value) { mutableStateOf(Rect.Zero) } - val courtZoomState = remember(showTree.value, selectedStepId) { ZoomState() } + remember(selectedStepId) { mutableStateMapOf() } 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, parentContent, type = info.type, modifier = courtModifier, - state = BasketCourtStates(stepOffsets, parentOffsets, courtArea, courtZoomState) + state = BasketCourtStates( + stepOffsets, + parentOffsets, + courtArea, + courtZoomState + ) ) if (showTree.value) { - StepsTree(root = stepsTree, + StepsTree( + root = stepsTree, selectedNodeId = selectedStepId, - onNodeSelected = { selectedStepId = it.id }) + onNodeSelected = { selectedStepId = it.id } + ) } } } diff --git a/app/src/main/java/com/iqball/app/serialization/EitherTypeAdapter.kt b/app/src/main/java/com/iqball/app/serialization/EitherTypeAdapter.kt index 6854a60..b09376a 100644 --- a/app/src/main/java/com/iqball/app/serialization/EitherTypeAdapter.kt +++ b/app/src/main/java/com/iqball/app/serialization/EitherTypeAdapter.kt @@ -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() + return null + } else -> throw JsonDataException("unexpected token : $token") }