diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 863a3eb..c2ac321 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.7.1" +agp = "8.9.2" android-compileSdk = "35" android-minSdk = "26" android-targetSdk = "35" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b810f5f..a4b9e1e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ -#Tue Oct 29 23:06:22 CST 2024 +#Fri May 02 19:34:04 ICT 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/compose/CompositionLocal.kt b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/compose/CompositionLocal.kt index 7f9c421..2370313 100644 --- a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/compose/CompositionLocal.kt +++ b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/compose/CompositionLocal.kt @@ -1,5 +1,9 @@ package org.jetbrains.compose.storytale.gallery.compose +import androidx.compose.runtime.staticCompositionLocalOf + inline fun noCompositionLocalProvided(): T { error("CompositionLocal ${T::class.simpleName} not present") } + +val LocalIsEmbeddedView = staticCompositionLocalOf { false } diff --git a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/FullStoryView.kt b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/FullStoryView.kt index 1398664..60a530f 100644 --- a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/FullStoryView.kt +++ b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/FullStoryView.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.DrawerState import androidx.compose.material3.DrawerValue @@ -32,9 +33,11 @@ import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -46,6 +49,7 @@ import androidx.navigation.toRoute import androidx.window.core.layout.WindowWidthSizeClass import kotlinx.coroutines.launch import org.jetbrains.compose.storytale.Story +import org.jetbrains.compose.storytale.gallery.compose.LocalIsEmbeddedView import org.jetbrains.compose.storytale.gallery.ui.theme.UseCustomDensity import org.jetbrains.compose.storytale.storiesStorage @@ -91,14 +95,20 @@ fun FullStorytaleGallery( popExitTransition = { ExitTransition.None }, ) { composable { + val showOverlayParameters = rememberSaveable { mutableStateOf(false) } Column(modifier = Modifier.fillMaxSize()) { GalleryTopAppBar( appState = appState, drawerState = drawerState, - activeStoryName = activeStoryItem.value?.story?.name, + activeStory = activeStoryItem.value?.story, + showOverlayParameters = showOverlayParameters, ) HorizontalDivider() - StoryContent(activeStoryItem.value?.story, modifier = Modifier.fillMaxSize()) + StoryContent( + activeStory = activeStoryItem.value?.story, + showOverlayParameters = showOverlayParameters, + modifier = Modifier.fillMaxSize(), + ) } } } @@ -177,12 +187,15 @@ private fun DrawerContent( ) } +@Suppress("ktlint:compose:mutable-state-param-check") @Composable private fun GalleryTopAppBar( drawerState: DrawerState, appState: StorytaleGalleryAppState, - activeStoryName: String?, + activeStory: Story?, + showOverlayParameters: MutableState, ) { + val activeStoryName = activeStory?.name.orEmpty() val coroutineScope = rememberCoroutineScope() val currentWindowWidthClass = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass val isExpanded = currentWindowWidthClass == WindowWidthSizeClass.EXPANDED @@ -190,7 +203,7 @@ private fun GalleryTopAppBar( CenterAlignedTopAppBar( title = { AnimatedContent(activeStoryName) { title -> - Text(title ?: "") + Text(title) } }, colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest), @@ -226,16 +239,36 @@ private fun GalleryTopAppBar( } }, actions = { + val widthClass = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass + + val isSmallWidth = widthClass == WindowWidthSizeClass.COMPACT + val useEmbeddedView = LocalIsEmbeddedView.current + ThemeSwitcherIconButton(appState) + AnimatedVisibility( + (isSmallWidth || useEmbeddedView) && activeStory?.parameters?.isNotEmpty() == true, + enter = fadeIn(), + exit = fadeOut(), + ) { + IconButton( + onClick = { + showOverlayParameters.value = true + }, + ) { + Icon(imageVector = Icons.Default.Settings, null) + } + } }, ) } @Composable internal fun ThemeSwitcherIconButton(appState: StorytaleGalleryAppState) { - IconButton(onClick = { - appState.switchTheme(!appState.isDarkTheme()) - }) { + IconButton( + onClick = { + appState.switchTheme(!appState.isDarkTheme()) + }, + ) { AnimatedContent(targetState = appState.isDarkTheme()) { isDarkTheme -> if (isDarkTheme) { Icon(imageVector = Light_mode, contentDescription = null) diff --git a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StoryContent.kt b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StoryContent.kt index 7bf7c8a..be785cb 100644 --- a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StoryContent.kt +++ b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StoryContent.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -46,6 +45,7 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput @@ -58,27 +58,26 @@ import dev.snipme.highlights.model.SyntaxThemes import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.jetbrains.compose.storytale.Story +import org.jetbrains.compose.storytale.gallery.compose.LocalIsEmbeddedView import org.jetbrains.compose.storytale.gallery.story.code.CodeBlock +@Suppress("ktlint:compose:mutable-state-param-check") @Composable internal fun StoryContent( activeStory: Story?, modifier: Modifier = Modifier, - useEmbeddedView: Boolean = false, + useEmbeddedView: Boolean = LocalIsEmbeddedView.current, + showOverlayParameters: MutableState = rememberSaveable { mutableStateOf(false) }, ) { - val widthClass = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass val heightClass = currentWindowAdaptiveInfo().windowSizeClass.windowHeightSizeClass val isSmallHeight = heightClass == WindowHeightSizeClass.COMPACT - val isSmallWidth = widthClass == WindowWidthSizeClass.COMPACT val useTabs = isSmallHeight || useEmbeddedView Box(modifier = modifier) { - val showOverlayParameters = remember { mutableStateOf(false) } - val previewContent = @Composable { - StoryPreview(showOverlayParameters, activeStory, useEmbeddedView, isSmallWidth) + StoryPreview(activeStory) } val codeContent: @Composable (BoxScope) -> Unit = { boxScope -> @@ -109,13 +108,9 @@ internal fun StoryContent( } } -@Suppress("ktlint:compose:mutable-state-param-check") @Composable private fun StoryPreview( - showOverlayParameters: MutableState, activeStory: Story? = null, - useEmbeddedView: Boolean = false, - isSmallWidth: Boolean = false, ) { Box(modifier = Modifier.fillMaxSize()) { Box( @@ -124,22 +119,6 @@ private fun StoryPreview( ) { activeStory?.content?.invoke(activeStory) } - - AnimatedVisibility( - (isSmallWidth || useEmbeddedView) && activeStory?.parameters?.isNotEmpty() == true, - enter = fadeIn(), - exit = fadeOut(), - modifier = Modifier.align(Alignment.BottomEnd), - ) { - SmallFloatingActionButton( - onClick = { - showOverlayParameters.value = true - }, - modifier = Modifier.padding(16.dp), - ) { - Icon(imageVector = Icons.Default.Settings, null) - } - } } } @@ -210,7 +189,10 @@ private fun StoryPreviewAndCodeStacked( .background(MaterialTheme.colorScheme.surfaceContainerLowest), ) { Column(modifier = Modifier.fillMaxHeight().weight(0.75f)) { - Box(modifier = Modifier.fillMaxSize().weight(0.5f), contentAlignment = Alignment.Center) { + Box( + modifier = Modifier.fillMaxSize().weight(0.5f), + contentAlignment = Alignment.Center, + ) { previewContent() } HorizontalDivider() @@ -227,7 +209,8 @@ private fun StoryPreviewAndCodeStacked( Column(modifier = Modifier.fillMaxHeight().widthIn(max = 250.dp)) { StoryParametersList( storyParameters!!, - modifier = Modifier.fillMaxSize().padding(8.dp).verticalScroll(rememberScrollState(0)), + modifier = Modifier.fillMaxSize().padding(8.dp) + .verticalScroll(rememberScrollState(0)), ) } } @@ -276,7 +259,8 @@ private fun BoxScope.OverlayParametersList( } StoryParametersList( activeStory?.parameters ?: emptyList(), - modifier = Modifier.fillMaxSize().padding(8.dp).verticalScroll(rememberScrollState(0)), + modifier = Modifier.fillMaxSize().padding(8.dp) + .verticalScroll(rememberScrollState(0)), ) } } @@ -302,7 +286,8 @@ private fun StoryTabs( }, modifier = Modifier.height(48.dp), ) { - val textColor = if (selectedTabIndex.value == 0) selectedTextColor else unselectedTextColor + val textColor = + if (selectedTabIndex.value == 0) selectedTextColor else unselectedTextColor Text(text = "Preview", style = MaterialTheme.typography.titleSmall, color = textColor) } Tab( @@ -312,7 +297,8 @@ private fun StoryTabs( onCodeTabClick() }, ) { - val textColor = if (selectedTabIndex.value == 1) selectedTextColor else unselectedTextColor + val textColor = + if (selectedTabIndex.value == 1) selectedTextColor else unselectedTextColor Text(text = "Code", style = MaterialTheme.typography.titleSmall, color = textColor) } } diff --git a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StorytaleGalleryApp.kt b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StorytaleGalleryApp.kt index 26c818a..1df74e6 100644 --- a/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StorytaleGalleryApp.kt +++ b/modules/gallery/src/commonMain/kotlin/org/jetbrains/compose/storytale/gallery/material3/StorytaleGalleryApp.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Density import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController +import org.jetbrains.compose.storytale.gallery.compose.LocalIsEmbeddedView import org.jetbrains.compose.storytale.gallery.ui.theme.LocalCustomDensity @Composable @@ -29,6 +30,7 @@ fun StorytaleGalleryApp( CompositionLocalProvider( LocalCustomDensity provides Density(LocalDensity.current.density * 0.8f), + LocalIsEmbeddedView provides isEmbedded, ) { MaterialTheme(colorScheme = if (appState.isDarkTheme()) darkThemeColors else lightThemeColors) { if (isEmbedded) { diff --git a/modules/runtime-api/src/commonMain/kotlin/org/jetbrains/compose/storytale/Story.kt b/modules/runtime-api/src/commonMain/kotlin/org/jetbrains/compose/storytale/Story.kt index 2d2be06..ee60801 100644 --- a/modules/runtime-api/src/commonMain/kotlin/org/jetbrains/compose/storytale/Story.kt +++ b/modules/runtime-api/src/commonMain/kotlin/org/jetbrains/compose/storytale/Story.kt @@ -1,6 +1,7 @@ package org.jetbrains.compose.storytale import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateMapOf import kotlin.enums.enumEntries data class Story( @@ -11,7 +12,7 @@ data class Story( val content: @Composable Story.() -> Unit, ) { @PublishedApi - internal val nameToParameterMapping = linkedMapOf>() // using linkedMap to keep the order + internal val nameToParameterMapping = mutableStateMapOf>() val parameters inline get() = nameToParameterMapping.values.toList() inline fun parameter(defaultValue: T) = StoryParameterDelegate(this, T::class, defaultValue)