diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d33f43..46f8e56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [1.0.0-beta16] - 2026-08-18 + +### Features +- New `ProgressIndicatorStyle.PILL`: the current step in a multi-step progress indicator renders as an elongated pill while the other steps stay small dots, like an iOS-style page indicator. `CoachmarkConfig.progressIndicatorStyle` (default `DOTS`, preserving the existing fill-up-to-current-step look) and `CoachmarkConfig.progressActivePillWidth` control it + +### Sample App +- Added a "Start Tour (Pill)" button to the Multi-Step Sequence example to demo `ProgressIndicatorStyle.PILL` + ## [1.0.0-beta15] - 2026-03-18 ### Features diff --git a/lumen/api/android/lumen.api b/lumen/api/android/lumen.api index 69e1a26..ac8694e 100644 --- a/lumen/api/android/lumen.api +++ b/lumen/api/android/lumen.api @@ -72,8 +72,8 @@ public final class io/luminos/CoachmarkColorsKt { public final class io/luminos/CoachmarkConfig { public static final field $stable I - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-D9Ej5fM ()F public final fun component10 ()I public final fun component11 ()I @@ -81,39 +81,41 @@ public final class io/luminos/CoachmarkConfig { public final fun component13 ()Lio/luminos/ScrimOpacity; public final fun component14 ()Lio/luminos/ScrimTapBehavior; public final fun component15 ()Z - public final fun component16 ()Z - public final fun component17 ()Lio/luminos/BackPressBehavior; - public final fun component18 ()Lio/luminos/HighlightAnimation; - public final fun component19 ()I + public final fun component16 ()Lio/luminos/ProgressIndicatorStyle; + public final fun component17-D9Ej5fM ()F + public final fun component18 ()Z + public final fun component19 ()Lio/luminos/BackPressBehavior; public final fun component2-D9Ej5fM ()F - public final fun component20 ()Z - public final fun component21 ()Ljava/lang/String; - public final fun component22 ()J - public final fun component23 ()Z + public final fun component20 ()Lio/luminos/HighlightAnimation; + public final fun component21 ()I + public final fun component22 ()Z + public final fun component23 ()Ljava/lang/String; public final fun component24 ()J - public final fun component25 ()Ljava/lang/String; + public final fun component25 ()Z public final fun component26 ()J - public final fun component27 ()Z - public final fun component28-D9Ej5fM ()F - public final fun component29-D9Ej5fM ()F + public final fun component27 ()Ljava/lang/String; + public final fun component28 ()J + public final fun component29 ()Z public final fun component3-D9Ej5fM ()F public final fun component30-D9Ej5fM ()F public final fun component31-D9Ej5fM ()F - public final fun component32-e0LSkKk ()I - public final fun component33-e0LSkKk ()I + public final fun component32-D9Ej5fM ()F + public final fun component33-D9Ej5fM ()F public final fun component34-e0LSkKk ()I public final fun component35-e0LSkKk ()I public final fun component36-e0LSkKk ()I - public final fun component37 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; - public final fun component38 ()Z + public final fun component37-e0LSkKk ()I + public final fun component38-e0LSkKk ()I + public final fun component39 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; public final fun component4 ()F + public final fun component40 ()Z public final fun component5 ()Lkotlin/jvm/functions/Function3; public final fun component6-D9Ej5fM ()F public final fun component7-D9Ej5fM ()F public final fun component8-D9Ej5fM ()F public final fun component9-D9Ej5fM ()F - public final fun copy-ZvfVXdg (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; - public static synthetic fun copy-ZvfVXdg$default (Lio/luminos/CoachmarkConfig;FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; + public final fun copy-C8SqqoU (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; + public static synthetic fun copy-C8SqqoU$default (Lio/luminos/CoachmarkConfig;FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; public fun equals (Ljava/lang/Object;)Z public final fun getBackPressBehavior ()Lio/luminos/BackPressBehavior; public final fun getConnectorAnimationDuration ()I @@ -133,6 +135,8 @@ public final class io/luminos/CoachmarkConfig { public final fun getDontShowAgainTextAlign-e0LSkKk ()I public final fun getFadeAnimationDuration ()I public final fun getHighlightAnimation ()Lio/luminos/HighlightAnimation; + public final fun getProgressActivePillWidth-D9Ej5fM ()F + public final fun getProgressIndicatorStyle ()Lio/luminos/ProgressIndicatorStyle; public final fun getPulseDurationMs ()I public final fun getRetrySkippedTargets ()Z public final fun getScrimOpacity ()Lio/luminos/ScrimOpacity; @@ -323,7 +327,7 @@ public final class io/luminos/CoachmarkTarget { } public final class io/luminos/CoachmarkTooltipKt { - public static final fun CoachmarkTooltip-eWZOFlk (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILio/luminos/CoachmarkColors;FZZZLjava/lang/String;ZLjava/lang/String;ZLkotlin/jvm/functions/Function1;FFFLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;IIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZJFFLkotlin/jvm/functions/Function1;ZLandroidx/compose/runtime/Composer;IIIIII)V + public static final fun CoachmarkTooltip-CyZyjUA (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILio/luminos/CoachmarkColors;FZLio/luminos/ProgressIndicatorStyle;FZZLjava/lang/String;ZLjava/lang/String;ZLkotlin/jvm/functions/Function1;FFFLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;IIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZJFFLkotlin/jvm/functions/Function1;ZLandroidx/compose/runtime/Composer;IIIIII)V } public final class io/luminos/ConnectorEndStyle : java/lang/Enum { @@ -465,6 +469,14 @@ public final class io/luminos/OverlayCoordinatorKt { public static final fun rememberOverlayCoordinator (Landroidx/compose/runtime/Composer;I)Lio/luminos/OverlayCoordinator; } +public final class io/luminos/ProgressIndicatorStyle : java/lang/Enum { + public static final field DOTS Lio/luminos/ProgressIndicatorStyle; + public static final field PILL Lio/luminos/ProgressIndicatorStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lio/luminos/ProgressIndicatorStyle; + public static fun values ()[Lio/luminos/ProgressIndicatorStyle; +} + public final class io/luminos/ScrimOpacity : java/lang/Enum { public static final field DARK Lio/luminos/ScrimOpacity; public static final field EXTRA_DARK Lio/luminos/ScrimOpacity; diff --git a/lumen/api/jvm/lumen.api b/lumen/api/jvm/lumen.api index f7942da..1294528 100644 --- a/lumen/api/jvm/lumen.api +++ b/lumen/api/jvm/lumen.api @@ -68,8 +68,8 @@ public final class io/luminos/CoachmarkColorsKt { public final class io/luminos/CoachmarkConfig { public static final field $stable I - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-D9Ej5fM ()F public final fun component10 ()I public final fun component11 ()I @@ -77,39 +77,41 @@ public final class io/luminos/CoachmarkConfig { public final fun component13 ()Lio/luminos/ScrimOpacity; public final fun component14 ()Lio/luminos/ScrimTapBehavior; public final fun component15 ()Z - public final fun component16 ()Z - public final fun component17 ()Lio/luminos/BackPressBehavior; - public final fun component18 ()Lio/luminos/HighlightAnimation; - public final fun component19 ()I + public final fun component16 ()Lio/luminos/ProgressIndicatorStyle; + public final fun component17-D9Ej5fM ()F + public final fun component18 ()Z + public final fun component19 ()Lio/luminos/BackPressBehavior; public final fun component2-D9Ej5fM ()F - public final fun component20 ()Z - public final fun component21 ()Ljava/lang/String; - public final fun component22 ()J - public final fun component23 ()Z + public final fun component20 ()Lio/luminos/HighlightAnimation; + public final fun component21 ()I + public final fun component22 ()Z + public final fun component23 ()Ljava/lang/String; public final fun component24 ()J - public final fun component25 ()Ljava/lang/String; + public final fun component25 ()Z public final fun component26 ()J - public final fun component27 ()Z - public final fun component28-D9Ej5fM ()F - public final fun component29-D9Ej5fM ()F + public final fun component27 ()Ljava/lang/String; + public final fun component28 ()J + public final fun component29 ()Z public final fun component3-D9Ej5fM ()F public final fun component30-D9Ej5fM ()F public final fun component31-D9Ej5fM ()F - public final fun component32-e0LSkKk ()I - public final fun component33-e0LSkKk ()I + public final fun component32-D9Ej5fM ()F + public final fun component33-D9Ej5fM ()F public final fun component34-e0LSkKk ()I public final fun component35-e0LSkKk ()I public final fun component36-e0LSkKk ()I - public final fun component37 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; - public final fun component38 ()Z + public final fun component37-e0LSkKk ()I + public final fun component38-e0LSkKk ()I + public final fun component39 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; public final fun component4 ()F + public final fun component40 ()Z public final fun component5 ()Lkotlin/jvm/functions/Function3; public final fun component6-D9Ej5fM ()F public final fun component7-D9Ej5fM ()F public final fun component8-D9Ej5fM ()F public final fun component9-D9Ej5fM ()F - public final fun copy-ZvfVXdg (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; - public static synthetic fun copy-ZvfVXdg$default (Lio/luminos/CoachmarkConfig;FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; + public final fun copy-C8SqqoU (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; + public static synthetic fun copy-C8SqqoU$default (Lio/luminos/CoachmarkConfig;FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZLio/luminos/ProgressIndicatorStyle;FZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; public fun equals (Ljava/lang/Object;)Z public final fun getBackPressBehavior ()Lio/luminos/BackPressBehavior; public final fun getConnectorAnimationDuration ()I @@ -129,6 +131,8 @@ public final class io/luminos/CoachmarkConfig { public final fun getDontShowAgainTextAlign-e0LSkKk ()I public final fun getFadeAnimationDuration ()I public final fun getHighlightAnimation ()Lio/luminos/HighlightAnimation; + public final fun getProgressActivePillWidth-D9Ej5fM ()F + public final fun getProgressIndicatorStyle ()Lio/luminos/ProgressIndicatorStyle; public final fun getPulseDurationMs ()I public final fun getRetrySkippedTargets ()Z public final fun getScrimOpacity ()Lio/luminos/ScrimOpacity; @@ -319,7 +323,7 @@ public final class io/luminos/CoachmarkTarget { } public final class io/luminos/CoachmarkTooltipKt { - public static final fun CoachmarkTooltip-eWZOFlk (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILio/luminos/CoachmarkColors;FZZZLjava/lang/String;ZLjava/lang/String;ZLkotlin/jvm/functions/Function1;FFFLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;IIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZJFFLkotlin/jvm/functions/Function1;ZLandroidx/compose/runtime/Composer;IIIIII)V + public static final fun CoachmarkTooltip-CyZyjUA (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILio/luminos/CoachmarkColors;FZLio/luminos/ProgressIndicatorStyle;FZZLjava/lang/String;ZLjava/lang/String;ZLkotlin/jvm/functions/Function1;FFFLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;IIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZJFFLkotlin/jvm/functions/Function1;ZLandroidx/compose/runtime/Composer;IIIIII)V } public final class io/luminos/ConnectorEndStyle : java/lang/Enum { @@ -476,6 +480,14 @@ public final class io/luminos/PreferencesCoachmarkStorage : io/luminos/Coachmark public fun remove (Ljava/lang/String;)V } +public final class io/luminos/ProgressIndicatorStyle : java/lang/Enum { + public static final field DOTS Lio/luminos/ProgressIndicatorStyle; + public static final field PILL Lio/luminos/ProgressIndicatorStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lio/luminos/ProgressIndicatorStyle; + public static fun values ()[Lio/luminos/ProgressIndicatorStyle; +} + public final class io/luminos/ScrimOpacity : java/lang/Enum { public static final field DARK Lio/luminos/ScrimOpacity; public static final field EXTRA_DARK Lio/luminos/ScrimOpacity; diff --git a/lumen/build.gradle.kts b/lumen/build.gradle.kts index 3edea9a..a11daa5 100644 --- a/lumen/build.gradle.kts +++ b/lumen/build.gradle.kts @@ -106,7 +106,7 @@ android { } } -val libraryVersion = "1.0.0-beta15" +val libraryVersion = "1.0.0-beta16" val libraryGroup = "io.github.aldefy" val libraryArtifact = "lumen" diff --git a/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkTooltipScreenshotTest.kt b/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkTooltipScreenshotTest.kt index 9ab4527..f609e16 100644 --- a/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkTooltipScreenshotTest.kt +++ b/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkTooltipScreenshotTest.kt @@ -17,6 +17,7 @@ import com.github.takahirom.roborazzi.captureRoboImage import io.luminos.CoachmarkTooltip import io.luminos.DarkCoachmarkColors import io.luminos.LightCoachmarkColors +import io.luminos.ProgressIndicatorStyle import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -83,6 +84,27 @@ class CoachmarkTooltipScreenshotTest { capture("tooltip_multiStep_step2of4") } + @Test + fun tooltip_multiStep_pillIndicator() { + composeTestRule.setContent { + MaterialTheme(colorScheme = lightColorScheme()) { + Box(modifier = Modifier.background(Color(0xFF1A1A2E)).padding(16.dp)) { + CoachmarkTooltip( + title = "Step 2", + description = "The current step renders as a pill; other steps stay dots.", + ctaText = "Next", + currentStep = 2, + totalSteps = 4, + colors = LightCoachmarkColors, + progressIndicatorStyle = ProgressIndicatorStyle.PILL, + onCtaClick = {}, + ) + } + } + } + capture("tooltip_multiStep_pillIndicator") + } + @Test fun tooltip_withSkipButton() { composeTestRule.setContent { diff --git a/lumen/src/androidUnitTest/snapshots/tooltip_multiStep_pillIndicator.png b/lumen/src/androidUnitTest/snapshots/tooltip_multiStep_pillIndicator.png new file mode 100644 index 0000000..47c3f00 Binary files /dev/null and b/lumen/src/androidUnitTest/snapshots/tooltip_multiStep_pillIndicator.png differ diff --git a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkScrim.kt b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkScrim.kt index fdddefb..e88e6b1 100644 --- a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkScrim.kt +++ b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkScrim.kt @@ -197,6 +197,10 @@ data class CoachmarkConfig( val scrimTapBehavior: ScrimTapBehavior = ScrimTapBehavior.DISMISS, /** Whether to show progress indicator dots for multi-step sequences */ val showProgressIndicator: Boolean = true, + /** Visual style for the progress indicator: small dots, or a pill for the current step */ + val progressIndicatorStyle: ProgressIndicatorStyle = ProgressIndicatorStyle.DOTS, + /** Width of the active pill when [progressIndicatorStyle] is [ProgressIndicatorStyle.PILL] */ + val progressActivePillWidth: Dp = 20.dp, /** Whether to wrap the tooltip content in a card/box */ val showTooltipCard: Boolean = false, /** Behavior when user presses the back button */ @@ -1049,6 +1053,8 @@ private fun BoxScope.TooltipContainer( colors = colors, cornerRadius = config.tooltipCornerRadius, showProgressIndicator = showProgressIndicator, + progressIndicatorStyle = config.progressIndicatorStyle, + progressActivePillWidth = config.progressActivePillWidth, showCard = config.showTooltipCard, showSkipButton = config.showSkipButton, skipButtonText = config.skipButtonText, diff --git a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt index f0992e9..d45b485 100644 --- a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt +++ b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt @@ -195,6 +195,24 @@ enum class TargetTapBehavior { BOTH, } +/** + * Visual style for the multi-step progress indicator shown in the tooltip footer. + * + * @see CoachmarkConfig.progressIndicatorStyle + */ +enum class ProgressIndicatorStyle { + /** Every step is a small circle; steps up to and including the current one are filled with [CoachmarkColors.progressActiveColor] (default). */ + DOTS, + + /** + * The current step is an elongated pill; every other step remains a small circle in + * [CoachmarkColors.progressInactiveColor], like an iOS-style page indicator. + * + * Sized via [CoachmarkConfig.progressActivePillWidth]. + */ + PILL, +} + /** Visual style at the endpoint of the connector line. */ enum class ConnectorEndStyle { /** Small filled circle (current default). */ diff --git a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTooltip.kt b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTooltip.kt index 4a93c15..b3e5954 100644 --- a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTooltip.kt +++ b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTooltip.kt @@ -55,6 +55,8 @@ import androidx.compose.ui.unit.sp * @param colors Theme colors * @param cornerRadius Corner radius for the card * @param showProgressIndicator Whether to show progress dots for multi-step sequences + * @param progressIndicatorStyle Visual style for the progress indicator (dots, or a pill for the current step) + * @param progressActivePillWidth Width of the active pill when [progressIndicatorStyle] is [ProgressIndicatorStyle.PILL] * @param showCard Whether to wrap content in a card/box background * @param showSkipButton Whether to show a skip button to dismiss the entire sequence * @param skipButtonText Text for the skip button @@ -78,6 +80,8 @@ fun CoachmarkTooltip( colors: CoachmarkColors, cornerRadius: Dp = 16.dp, showProgressIndicator: Boolean = true, + progressIndicatorStyle: ProgressIndicatorStyle = ProgressIndicatorStyle.DOTS, + progressActivePillWidth: Dp = 20.dp, showCard: Boolean = false, showSkipButton: Boolean = false, skipButtonText: String = "Skip", @@ -271,6 +275,8 @@ fun CoachmarkTooltip( totalSteps = totalSteps, activeColor = colors.progressActiveColor, inactiveColor = colors.progressInactiveColor, + style = progressIndicatorStyle, + activePillWidth = progressActivePillWidth, ) } @@ -380,6 +386,8 @@ private fun ProgressIndicator( totalSteps: Int, activeColor: Color, inactiveColor: Color, + style: ProgressIndicatorStyle = ProgressIndicatorStyle.DOTS, + activePillWidth: Dp = 20.dp, modifier: Modifier = Modifier, ) { Row( @@ -392,14 +400,29 @@ private fun ProgressIndicator( verticalAlignment = Alignment.CenterVertically, ) { repeat(totalSteps) { index -> - val isActive = index < currentStep - Box( - modifier = - Modifier - .size(8.dp) - .clip(CircleShape) - .background(if (isActive) activeColor else inactiveColor), - ) + when (style) { + ProgressIndicatorStyle.DOTS -> { + val isActive = index < currentStep + Box( + modifier = + Modifier + .size(8.dp) + .clip(CircleShape) + .background(if (isActive) activeColor else inactiveColor), + ) + } + + ProgressIndicatorStyle.PILL -> { + val isCurrent = index == currentStep - 1 + Box( + modifier = + Modifier + .size(width = if (isCurrent) activePillWidth else 8.dp, height = 8.dp) + .clip(RoundedCornerShape(4.dp)) + .background(if (isCurrent) activeColor else inactiveColor), + ) + } + } } } } diff --git a/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt b/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt index f598093..00b2489 100644 --- a/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt +++ b/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt @@ -178,6 +178,14 @@ class CoachmarkTargetTest { assertTrue(values.contains(ConnectorEndStyle.CUSTOM)) } + @Test + fun progressIndicatorStyle_values() { + val values = ProgressIndicatorStyle.entries + assertEquals(2, values.size) + assertTrue(values.contains(ProgressIndicatorStyle.DOTS)) + assertTrue(values.contains(ProgressIndicatorStyle.PILL)) + } + // ── TargetTapBehavior ────────────────────────────────────────────────── @Test diff --git a/sample/src/commonMain/kotlin/com/aditlal/sample/examples/SequenceExample.kt b/sample/src/commonMain/kotlin/com/aditlal/sample/examples/SequenceExample.kt index 3298b7b..7c6c38b 100644 --- a/sample/src/commonMain/kotlin/com/aditlal/sample/examples/SequenceExample.kt +++ b/sample/src/commonMain/kotlin/com/aditlal/sample/examples/SequenceExample.kt @@ -40,6 +40,7 @@ import io.luminos.CoachmarkTarget import io.luminos.ConnectorStyle import io.luminos.CutoutShape import io.luminos.HighlightAnimation +import io.luminos.ProgressIndicatorStyle import io.luminos.ScrimTapBehavior import io.luminos.coachmarkColors import io.luminos.coachmarkTarget @@ -54,6 +55,7 @@ fun SequenceExample( val controller = rememberCoachmarkController() var showSequence by remember { mutableStateOf(false) } var selectedTab by remember { mutableIntStateOf(0) } + var progressIndicatorStyle by remember { mutableStateOf(ProgressIndicatorStyle.DOTS) } CoachmarkHost( controller = controller, @@ -63,6 +65,7 @@ fun SequenceExample( skipButtonText = "Skip tour", backPressBehavior = BackPressBehavior.NAVIGATE, scrimTapBehavior = ScrimTapBehavior.NONE, + progressIndicatorStyle = progressIndicatorStyle, ), colors = coachmarkColors(), ) { @@ -159,10 +162,24 @@ fun SequenceExample( Row( horizontalArrangement = Arrangement.spacedBy(16.dp), ) { - Button(onClick = { showSequence = true }) { + Button( + onClick = { + progressIndicatorStyle = ProgressIndicatorStyle.DOTS + showSequence = true + }, + ) { Text("Start Tour") } + Button( + onClick = { + progressIndicatorStyle = ProgressIndicatorStyle.PILL + showSequence = true + }, + ) { + Text("Start Tour (Pill)") + } + Button(onClick = { controller.dismiss() }) { Text("Dismiss") }