@@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.StrokeCap
1919import androidx.compose.ui.graphics.drawscope.Stroke
2020import androidx.compose.ui.graphics.drawscope.rotate
2121import androidx.compose.ui.input.pointer.pointerInput
22+ import androidx.compose.ui.layout.LayoutCoordinates
2223import androidx.compose.ui.layout.onGloballyPositioned
2324import androidx.compose.ui.layout.positionInRoot
2425import androidx.compose.ui.platform.LocalDensity
@@ -97,7 +98,9 @@ fun ShowcaseLayout(
9798 }
9899 val currentContent by rememberUpdatedState(content)
99100 val resetDelay by derivedStateOf { animationDuration.toLong() + INDEX_RESET_DELAY }
101+ val layoutCoordinatesState = remember { mutableStateOf<LayoutCoordinates ?>(null ) }
100102 val scope = ShowcaseScopeImpl (greeting)
103+ scope.layoutCoordinatesState = layoutCoordinatesState
101104 scope.currentContent()
102105
103106 var singleGreetingMsg by remember { mutableStateOf<ShowcaseMsg ?>(null ) }
@@ -133,7 +136,10 @@ fun ShowcaseLayout(
133136 }
134137 }
135138
136- BoxWithConstraints (modifier = Modifier .fillMaxSize()) {
139+ BoxWithConstraints (modifier = Modifier
140+ .fillMaxSize()
141+ .onGloballyPositioned { layoutCoordinatesState.value = it }
142+ ) {
137143 val coroutineScope = rememberCoroutineScope()
138144 if (isShowcasing || showCasingItem || isSingleGreeting) {
139145 val offset by animateOffsetAsState(
@@ -1014,6 +1020,10 @@ fun ShowcaseLayout(
10141020
10151021class ShowcaseScopeImpl (greeting : ShowcaseMsg ? ) : ShowcaseScope {
10161022 private val showcaseDataHashMap = HashMap <Int , ShowcaseData >()
1023+
1024+ /* * Coordinates of the showcase layout itself, used to convert target positions from
1025+ * root space to the layout's local space (the space the overlay canvas draws in). */
1026+ internal var layoutCoordinatesState: State <LayoutCoordinates ?> = mutableStateOf(null )
10171027 override var showcaseEventListener: ShowcaseEventListener ? = null
10181028 private val _showcaseActionFlow = MutableStateFlow <Int ?>(null )
10191029 val showcaseActionFlow = _showcaseActionFlow .asStateFlow()
@@ -1108,11 +1118,22 @@ class ShowcaseScopeImpl(greeting: ShowcaseMsg?) : ShowcaseScope {
11081118 }
11091119
11101120 fun getPositionFor (index : Int ): Offset {
1111- if (index == 0 ) {
1112- return showcaseDataHashMap[1 ]?.position ? : Offset (0f , 0f )
1121+ val data = showcaseDataHashMap[if (index == 0 ) 1 else index] ? : return Offset (0f , 0f )
1122+ return positionInLayout(data)
1123+ }
1124+
1125+ /* * The stored position is relative to the composition root, but the overlay canvas draws
1126+ * in the layout's local space. When the layout doesn't sit at the root origin (status bar
1127+ * padding, app bars, ...) the two spaces differ, so convert; the raw root position is only
1128+ * a fallback for when either set of coordinates is detached. */
1129+ private fun positionInLayout (data : ShowcaseData ): Offset {
1130+ val layoutCoordinates = layoutCoordinatesState.value
1131+ val targetCoordinates = data.coordinates
1132+ return if (layoutCoordinates?.isAttached == true && targetCoordinates?.isAttached == true ) {
1133+ layoutCoordinates.localPositionOf(targetCoordinates, Offset .Zero )
1134+ } else {
1135+ data.position
11131136 }
1114- val p = showcaseDataHashMap[index]?.position ? : Offset (0f , 0f )
1115- return p
11161137 }
11171138
11181139 fun getHashMapSize (): Int {
0 commit comments