diff --git a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/UnifyCoachmark.kt b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/UnifyCoachmark.kt index 3c0e075..c3a5d82 100644 --- a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/UnifyCoachmark.kt +++ b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/UnifyCoachmark.kt @@ -11,6 +11,7 @@ import com.pseudoankit.coachmark.model.OverlayClickEvent import com.pseudoankit.coachmark.overlay.UnifyOverlayEffect import com.pseudoankit.coachmark.scope.CoachMarkScope import com.pseudoankit.coachmark.scope.CoachMarkScopeImpl +import com.pseudoankit.coachmark.scope.enableCoachMark import com.pseudoankit.coachmark.ui.CoachMarkImpl import com.pseudoankit.coachmark.util.CoachMarkDefaults import com.pseudoankit.coachmark.util.CoachMarkKey @@ -18,6 +19,41 @@ import com.pseudoankit.coachmark.util.CoachMarkKey public val LocalCoachMarkScope: ProvidableCompositionLocal = compositionLocalOf { error("CompositionLocal CoachMarkScope not present") } +/** + * Entry point to show coachmark, + * This screen should to be called at the root level so that coachmark can be visible at very top + * @param overlayEffect configure overlay effect to be shown when tooltip is visible + * @param content actual screen content + */ +@Composable +public fun UnifyCoachmark( + overlayEffect: UnifyOverlayEffect = CoachMarkDefaults.Overlay.background, + onOverlayClicked: (CoachMarkKey) -> OverlayClickEvent = { CoachMarkDefaults.Overlay.clickEvent }, + content: @Composable CoachMarkScope.() -> Unit +) { + UnifyCoachmark( + tooltip = {}, + overlayEffect = overlayEffect, + onOverlayClicked = onOverlayClicked, + content = content + ) +} + +@Deprecated( + message = "Please avoid passing tooltip at top level, tooltips can be simply passed when calling `enableCoachMark` for each view", +) +/** + * This method is deprecated, not recommended to pass tooltip at top level + * as now it can be passed when calling [enableCoachMark] method for each view + * for backward compatibility [tooltip] field will still work as expected + * but [enableCoachMark]'s tooltip will take priority over [tooltip] if passed. + * + * Entry point to show coachmark, + * This screen should to be called at the root level so that coachmark can be visible at very top + * @param tooltip view to be shown when any view is highlighted + * @param overlayEffect configure overlay effect to be shown when tooltip is visible + * @param content actual screen content + */ @Composable public fun UnifyCoachmark( tooltip: @Composable CoachMarkScope.(CoachMarkKey) -> Unit, diff --git a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/demo/UnifyCoachmarkDemo.kt b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/demo/UnifyCoachmarkDemo.kt index a031d02..5263b08 100644 --- a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/demo/UnifyCoachmarkDemo.kt +++ b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/demo/UnifyCoachmarkDemo.kt @@ -42,22 +42,21 @@ public fun UnifyCoachmarkDemo() { ) { PlotTextsAndUseLocalCoachMarkScope() Button( - onClick = { - show(Keys.Text1) - }, + onClick = { show(Keys.Text1) }, modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = "Highlight 1") } Button( - onClick = { - show(*Keys.values()) - }, + onClick = { show(*Keys.values()) }, modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = "Highlight All") } - Button(onClick = { show(Keys.TextBottom, Keys.TextTop) }) { + Button( + onClick = { show(Keys.TextBottom, Keys.TextTop) }, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { Text(text = "Highlight Some") } } @@ -67,31 +66,50 @@ public fun UnifyCoachmarkDemo() { @Composable private fun ColumnScope.PlotTextsAndUseLocalCoachMarkScope() { - CoachMarkTargetText("Will show tooltip 1", Alignment.Start, Keys.Text1, ToolTipPlacement.End) - - CoachMarkTargetText("Will show tooltip 2", Alignment.Start, Keys.Text2, ToolTipPlacement.End) + CoachMarkTargetText( + text = "Will show tooltip 1", + alignment = Alignment.Start, + key = Keys.Text1, + placement = ToolTipPlacement.End, + tooltip = { + Balloon(arrow = Arrow.Start()) { + Text(text = "Highlighting Text1 at enableCoachmark method", color = Color.White) + } + } + ) CoachMarkTargetText( - "Will show tooltip to left", - Alignment.End, - Keys.TextStart, - ToolTipPlacement.Start + text = "Will show tooltip 2", + alignment = Alignment.Start, + key = Keys.Text2, + placement = ToolTipPlacement.End ) CoachMarkTargetText( - "Will show tooltip below", - Alignment.CenterHorizontally, - Keys.TextBottom, - ToolTipPlacement.Bottom + text = "Will show tooltip to left", + alignment = Alignment.End, + key = Keys.TextStart, + placement = ToolTipPlacement.Start, + tooltip = { + Balloon(arrow = Arrow.End()) { + Text(text = "tooltip to left at enableCoachmark method", color = Color.White) + } + } ) CoachMarkTargetText( - "Will show tooltip above", - Alignment.CenterHorizontally, - Keys.TextTop, - ToolTipPlacement.Top + text = "Will show tooltip below", + alignment = Alignment.CenterHorizontally, + key = Keys.TextBottom, + placement = ToolTipPlacement.Bottom ) + CoachMarkTargetText( + text = "Will show tooltip above", + alignment = Alignment.CenterHorizontally, + key = Keys.TextTop, + placement = ToolTipPlacement.Top + ) } @Composable @@ -100,6 +118,7 @@ private fun ColumnScope.CoachMarkTargetText( alignment: Alignment.Horizontal, key: Keys, placement: ToolTipPlacement, + tooltip: @Composable (() -> Unit)? = null ) { val coachMarkScope = LocalCoachMarkScope.current @@ -114,7 +133,8 @@ private fun ColumnScope.CoachMarkTargetText( shape = HighlightedViewConfig.Shape.Rect(12.dp), padding = PaddingValues(8.dp) ), - coachMarkScope = coachMarkScope + coachMarkScope = coachMarkScope, + tooltip = tooltip ) .padding(16.dp), color = Color.Black @@ -126,31 +146,31 @@ private fun Tooltip(key: CoachMarkKey) { when (key) { Keys.Text1 -> { Balloon(arrow = Arrow.Start()) { - Text(text = "Highlighting Text1", color = Color.White) + Text(text = "Highlighting Text1 root level", color = Color.White) } } Keys.Text2 -> { Balloon(arrow = Arrow.Start()) { - Text(text = "Highlighting Text2", color = Color.White) + Text(text = "Highlighting Text2 root level", color = Color.White) } } Keys.TextStart -> { Balloon(arrow = Arrow.End()) { - Text(text = "A tooltip to the left", color = Color.White) + Text(text = "A tooltip to the left root level", color = Color.White) } } Keys.TextBottom -> { Balloon(arrow = Arrow.Top()) { - Text(text = "A tooltip below", color = Color.White) + Text(text = "A tooltip below root level", color = Color.White) } } Keys.TextTop -> { Balloon(arrow = Arrow.Bottom()) { - Text(text = "A tooltip above", color = Color.White) + Text(text = "A tooltip above root level", color = Color.White) } } } diff --git a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/model/TooltipConfig.kt b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/model/TooltipConfig.kt index d16e044..7f3286f 100644 --- a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/model/TooltipConfig.kt +++ b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/model/TooltipConfig.kt @@ -1,6 +1,7 @@ package com.pseudoankit.coachmark.model import androidx.compose.animation.core.AnimationSpec +import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import com.pseudoankit.coachmark.scope.CoachMarkScope import com.pseudoankit.coachmark.util.CoachMarkKey @@ -20,7 +21,8 @@ public data class TooltipConfig( val toolTipPlacement: ToolTipPlacement, val key: CoachMarkKey, val highlightedViewShape: HighlightedViewConfig.Shape, - val animationState: AnimationState + val animationState: AnimationState, + val tooltip: @Composable (() -> Unit)? = null ) { /** diff --git a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScope.kt b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScope.kt index d877008..5443b9a 100644 --- a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScope.kt +++ b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScope.kt @@ -1,6 +1,7 @@ package com.pseudoankit.coachmark.scope import androidx.compose.animation.core.AnimationSpec +import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import com.pseudoankit.coachmark.model.HighlightedViewConfig @@ -37,7 +38,8 @@ public interface CoachMarkScope { key: CoachMarkKey, toolTipPlacement: ToolTipPlacement, tooltipAnimationSpec: AnimationSpec = CoachMarkDefaults.ToolTip.animationSpec, - highlightedViewConfig: HighlightedViewConfig = HighlightedViewConfig() + highlightedViewConfig: HighlightedViewConfig = HighlightedViewConfig(), + tooltip: @Composable (() -> Unit)? = null ): Modifier /** @@ -70,12 +72,14 @@ public fun Modifier.enableCoachMark( key: CoachMarkKey, toolTipPlacement: ToolTipPlacement, tooltipAnimationSpec: AnimationSpec = CoachMarkDefaults.ToolTip.animationSpec, - highlightedViewConfig: HighlightedViewConfig = HighlightedViewConfig() + highlightedViewConfig: HighlightedViewConfig = HighlightedViewConfig(), + tooltip: @Composable (() -> Unit)? = null ): Modifier = with(coachMarkScope) { enableCoachMark( key = key, toolTipPlacement = toolTipPlacement, tooltipAnimationSpec = tooltipAnimationSpec, - highlightedViewConfig = highlightedViewConfig + highlightedViewConfig = highlightedViewConfig, + tooltip = tooltip ) } diff --git a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScopeImpl.kt b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScopeImpl.kt index 4ee63cb..cdca183 100644 --- a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScopeImpl.kt +++ b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/scope/CoachMarkScopeImpl.kt @@ -3,6 +3,7 @@ package com.pseudoankit.coachmark.scope import androidx.compose.animation.core.AnimationSpec import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -54,6 +55,7 @@ internal class CoachMarkScopeImpl( toolTipPlacement: ToolTipPlacement, tooltipAnimationSpec: AnimationSpec, highlightedViewConfig: HighlightedViewConfig, + tooltip: @Composable (() -> Unit)? ): Modifier = onGloballyPositioned { layoutCoordinates -> val startPadding = highlightedViewConfig.padding.calculateStartPadding(layoutDirection).toPx(density) @@ -74,7 +76,8 @@ internal class CoachMarkScopeImpl( highlightedViewShape = highlightedViewConfig.shape, animationState = TooltipConfig.AnimationState( tooltipAnimationSpec = tooltipAnimationSpec - ) + ), + tooltip = tooltip ) } diff --git a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/ui/CoachMarkImpl.kt b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/ui/CoachMarkImpl.kt index 5d493c5..94b4d6e 100644 --- a/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/ui/CoachMarkImpl.kt +++ b/coachmark/src/commonMain/kotlin/com/pseudoankit/coachmark/ui/CoachMarkImpl.kt @@ -63,13 +63,14 @@ internal fun CoachMarkImpl( tooltipHolder = currentTooltip, modifier = Modifier.layoutId(TooltipId.current), ) { - coachMarkScope.tooltip(it) + coachMarkScope.currentVisibleTooltip?.tooltip?.invoke() + ?: coachMarkScope.tooltip(it) } Tooltip( tooltipHolder = previousTooltip, modifier = Modifier.layoutId(TooltipId.previous), ) { - coachMarkScope.tooltip(it) + coachMarkScope.lastVisibleTooltip?.tooltip?.invoke() ?: coachMarkScope.tooltip(it) } } }