diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt index 6e8e568402..f549d5767c 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/style/Palette.kt @@ -19,6 +19,16 @@ object ImageviewerColors { val TranslucentWhite = Color(255, 255, 255, 20) val Transparent = Color.Transparent + val background = Color(0xFFFFFFFF) + val onBackground = Color(0xFF19191C) + + val fullScreenImageBackground = Color(0xFF19191C) + + val uiLightBlack = Color(25, 25, 28, 128) + val textOnImage = Color.White + val noteBlockBackground = Color(0xFFF3F3F4) + + val KotlinGradient0 = Color(0xFF7F52FF) val KotlinGradient50 = Color(0xFFC811E2) val KotlinGradient100 = Color(0xFFE54857) @@ -39,8 +49,8 @@ internal fun ImageViewerTheme(content: @Composable () -> Unit) { isSystemInDarkTheme() // todo check and change colors MaterialTheme( colorScheme = MaterialTheme.colorScheme.copy( - background = Color(0xFF1B1B1B), - onBackground = Color(0xFFFFFFFF) + background = ImageviewerColors.background, + onBackground = ImageviewerColors.onBackground ) ) { content() diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt index 043e51c672..7cf4a0ae4c 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/FullscreenImage.kt @@ -5,8 +5,10 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ImageBitmap @@ -57,7 +59,7 @@ internal fun FullscreenImage( null } } - Box(Modifier.fillMaxSize().background(color = MaterialTheme.colorScheme.background)) { + Box(Modifier.fillMaxSize().background(color = ImageviewerColors.fullScreenImageBackground)) { Column { FullscreenImageBar( localization, @@ -73,31 +75,52 @@ internal fun FullscreenImage( } }) if (imageWithFilter != null) { - val imageSize = IntSize(imageWithFilter.width, imageWithFilter.height) - val scalableState = remember(imageSize) { ScalableState(imageSize) } - val visiblePartOfImage: IntRect = scalableState.visiblePart - Slider( - modifier = Modifier.fillMaxWidth(), - value = scalableState.scale, - valueRange = MIN_SCALE..MAX_SCALE, - onValueChange = { scalableState.setScale(it) }, - ) - Box( - modifier = Modifier.fillMaxSize() - .onGloballyPositioned { coordinates -> - scalableState.changeBoxSize(coordinates.size) + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { + val imageSize = IntSize(imageWithFilter.width, imageWithFilter.height) + val scalableState = remember(imageSize) { ScalableState(imageSize) } + val visiblePartOfImage: IntRect = scalableState.visiblePart + Column { + Slider( + modifier = Modifier.fillMaxWidth(), + value = scalableState.scale, + valueRange = MIN_SCALE..MAX_SCALE, + onValueChange = { scalableState.setScale(it) }, + ) + Box( + modifier = Modifier.fillMaxSize() + .onGloballyPositioned { coordinates -> + scalableState.changeBoxSize(coordinates.size) + } + .addUserInput(scalableState) + ) { + Image( + modifier = Modifier.fillMaxSize(), + painter = BitmapPainter( + imageWithFilter, + srcOffset = visiblePartOfImage.topLeft, + srcSize = visiblePartOfImage.size + ), + contentDescription = null + ) + } + } + Box( + Modifier.clip(RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp)) + .background(ImageviewerColors.fullScreenImageBackground).padding(16.dp) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.padding(bottom = 16.dp) + ) { + FilterButtons(availableFilters, selectedFilters, { + if (it !in selectedFilters) { + selectedFilters += it + } else { + selectedFilters -= it + } + }) } - .addUserInput(scalableState) - ) { - Image( - modifier = Modifier.fillMaxSize(), - painter = BitmapPainter( - imageWithFilter, - srcOffset = visiblePartOfImage.topLeft, - srcSize = visiblePartOfImage.size - ), - contentDescription = null - ) + } } } else { LoadingScreen() @@ -118,7 +141,7 @@ private fun FullscreenImageBar( onSelectFilter: (FilterType) -> Unit ) { TopAppBar( - modifier = Modifier.background(brush = ImageviewerColors.kotlinHorizontalGradientBrush), + modifier = Modifier.background(color = ImageviewerColors.fullScreenImageBackground), colors = TopAppBarDefaults.smallTopAppBarColors( containerColor = ImageviewerColors.Transparent, titleContentColor = MaterialTheme.colorScheme.onBackground @@ -128,27 +151,30 @@ private fun FullscreenImageBar( }, navigationIcon = { Tooltip(localization.back) { - Image( - painterResource("back.png"), - contentDescription = null, - modifier = Modifier.size(38.dp) - .clip(CircleShape) - .clickable { onBack() } + CircularButton( + painterResource("arrowleft.png"), + onClick = { onBack() } ) } }, - actions = { - for (type in filters) { - FilterButton(active = type in selectedFilters, - type, - onClick = { - onSelectFilter(type) - }) - } - } ) } +@Composable +private fun FilterButtons( + filters: List, + selectedFilters: Set, + onSelectFilter: (FilterType) -> Unit +) { + for (type in filters) { + FilterButton(active = type in selectedFilters, + type, + onClick = { + onSelectFilter(type) + }) + } +} + @Composable private fun FilterButton( @@ -165,14 +191,13 @@ private fun FilterButton( Image( getFilterImage(active, type = type), contentDescription = null, - Modifier.size(38.dp) + Modifier.size(40.dp) .hoverable(interactionSource) .background(color = ImageviewerColors.buttonBackground(filterButtonHover)) .clickable { onClick() } ) } } - Spacer(Modifier.width(20.dp)) } @OptIn(ExperimentalResourceApi::class) diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt index a19c67304f..5f3d4fccfc 100755 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/GalleryScreen.kt @@ -1,9 +1,7 @@ package example.imageviewer.view -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -15,15 +13,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable @@ -32,14 +28,10 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import example.imageviewer.Dependencies import example.imageviewer.ExternalImageViewerEvent import example.imageviewer.model.GalleryEntryWithMetadata @@ -48,24 +40,9 @@ import example.imageviewer.model.GalleryPage import example.imageviewer.model.PhotoGallery import example.imageviewer.model.bigUrl import example.imageviewer.style.ImageviewerColors -import example.imageviewer.style.ImageviewerColors.kotlinHorizontalGradientBrush import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource -@Composable -internal fun GalleryHeader() { - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier.padding(10.dp).fillMaxWidth() - ) { - Text( - "My Gallery", - fontSize = 25.sp, - color = MaterialTheme.colorScheme.onBackground, - fontStyle = FontStyle.Italic - ) - } -} enum class GalleryStyle { SQUARES, @@ -91,32 +68,36 @@ internal fun GalleryScreen( } Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { - TitleBar( - onRefresh = { photoGallery.updatePictures() }, - onToggle = { galleryPage.toggleGalleryStyle() }, - dependencies - ) - if (needShowPreview()) { - PreviewImage( - getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, - picture = galleryPage.picture, onClick = { - galleryPage.pictureId?.let(onClickPreviewPicture) - }) - } - when (galleryPage.galleryStyle) { - GalleryStyle.SQUARES -> SquaresGalleryView( - pictures, - galleryPage.pictureId, - onSelect = { galleryPage.selectPicture(it) }, - onMakeNewMemory + Box { + if (needShowPreview()) { + PreviewImage( + getImage = { dependencies.imageRepository.loadContent(it.bigUrl) }, + picture = galleryPage.picture, onClick = { + galleryPage.pictureId?.let(onClickPreviewPicture) + }) + } + TitleBar( + onRefresh = { photoGallery.updatePictures() }, + onToggle = { galleryPage.toggleGalleryStyle() }, + dependencies ) + } + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { + when (galleryPage.galleryStyle) { + GalleryStyle.SQUARES -> SquaresGalleryView( + pictures, + galleryPage.pictureId, + onSelect = { galleryPage.selectPicture(it) }, + ) - GalleryStyle.LIST -> ListGalleryView( - pictures, - dependencies, - onSelect = { galleryPage.selectPicture(it) }, - onFullScreen = { onClickPreviewPicture(it) } - ) + GalleryStyle.LIST -> ListGalleryView( + pictures, + dependencies, + onSelect = { galleryPage.selectPicture(it) }, + onFullScreen = { onClickPreviewPicture(it) } + ) + } + MakeNewMemoryMiniature(onMakeNewMemory) } } if (pictures.isEmpty()) { @@ -129,54 +110,95 @@ private fun SquaresGalleryView( images: List, selectedImage: GalleryId?, onSelect: (GalleryId) -> Unit, - onMakeNewMemory: () -> Unit, ) { - LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { - item { - MakeNewMemoryMiniature(onMakeNewMemory) - } - itemsIndexed(images) { idx, image -> - val isSelected = image.id == selectedImage - val (picture, bitmap) = image - SquareMiniature( - image.thumbnail, - onClick = { onSelect(picture) }, - isHighlighted = isSelected - ) + Column { + Spacer(Modifier.height(1.dp)) + LazyVerticalGrid( + columns = GridCells.Adaptive(minSize = 130.dp), + verticalArrangement = Arrangement.spacedBy(1.dp), + horizontalArrangement = Arrangement.spacedBy(1.dp) + ) { + itemsIndexed(images) { idx, image -> + val isSelected = image.id == selectedImage + val (picture, bitmap) = image + SquareMiniature( + image.thumbnail, + onClick = { onSelect(picture) }, + isHighlighted = isSelected + ) + } } } } +@OptIn(ExperimentalResourceApi::class) @Composable private fun MakeNewMemoryMiniature(onClick: () -> Unit) { - Box( - Modifier.aspectRatio(1.0f) - .clickable { - onClick() - }, contentAlignment = Alignment.Center - ) { - Text( - "+", - modifier = Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.onBackground, - textAlign = TextAlign.Center, - fontSize = 50.sp - ) + Column { + Box( + Modifier + .clip(CircleShape) + .width(52.dp) + .background(ImageviewerColors.uiLightBlack) + .aspectRatio(1.0f) + .clickable { + onClick() + }, + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource("plus.png"), + contentDescription = null, + modifier = Modifier + .width(18.dp) + .height(18.dp), + ) + } + Spacer(Modifier.height(32.dp)) } } +@OptIn(ExperimentalResourceApi::class) @Composable internal fun SquareMiniature(image: ImageBitmap, isHighlighted: Boolean, onClick: () -> Unit) { - Image( - bitmap = image, - contentDescription = null, - modifier = Modifier.aspectRatio(1.0f).clickable { onClick() }.then( - if (isHighlighted) { - Modifier.border(BorderStroke(5.dp, Color.White)) - } else Modifier - ), - contentScale = ContentScale.Crop - ) + Box( + Modifier.aspectRatio(1.0f).clickable { onClick() }, + contentAlignment = Alignment.BottomEnd + ) { + Image( + bitmap = image, + contentDescription = null, + modifier = Modifier.fillMaxSize().clickable { onClick() }.then( + if (isHighlighted) { + Modifier//.border(BorderStroke(5.dp, Color.White)) + } else Modifier + ), + contentScale = ContentScale.Crop + ) + if (isHighlighted) { + Box(Modifier.fillMaxSize().background(ImageviewerColors.uiLightBlack)) + Box( + Modifier + .padding(end = 4.dp, bottom = 4.dp) + .clip(CircleShape) + .width(32.dp) + .background(ImageviewerColors.uiLightBlack) + .aspectRatio(1.0f) + .clickable { + onClick() + }, + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource("eye.png"), + contentDescription = null, + modifier = Modifier + .width(17.dp) + .height(17.dp), + ) + } + } + } } @Composable @@ -186,8 +208,6 @@ private fun ListGalleryView( onSelect: (GalleryId) -> Unit, onFullScreen: (GalleryId) -> Unit ) { - GalleryHeader() - Spacer(modifier = Modifier.height(10.dp)) ScrollableColumn( modifier = Modifier.fillMaxSize() ) { @@ -215,44 +235,14 @@ private fun ListGalleryView( @Composable private fun TitleBar(onRefresh: () -> Unit, onToggle: () -> Unit, dependencies: Dependencies) { TopAppBar( - modifier = Modifier.background(brush = kotlinHorizontalGradientBrush), + modifier = Modifier.padding(start = 12.dp, end = 12.dp), colors = TopAppBarDefaults.smallTopAppBarColors( containerColor = ImageviewerColors.Transparent, titleContentColor = MaterialTheme.colorScheme.onBackground ), title = { - Row(Modifier.height(50.dp)) { - Text( - dependencies.localization.appName, - modifier = Modifier.weight(1f).align(Alignment.CenterVertically), - fontWeight = FontWeight.Bold - ) - Surface( - color = ImageviewerColors.Transparent, - modifier = Modifier.padding(end = 20.dp).align(Alignment.CenterVertically), - shape = CircleShape - ) { - Image( - painter = painterResource("list_view.png"), - contentDescription = null, - modifier = Modifier.size(35.dp).clickable { - onToggle() - } - ) - } - Surface( - color = ImageviewerColors.Transparent, - modifier = Modifier.padding(end = 20.dp).align(Alignment.CenterVertically), - shape = CircleShape - ) { - Image( - painter = painterResource("refresh.png"), - contentDescription = null, - modifier = Modifier.size(35.dp).clickable { - onRefresh() - } - ) - } + Row(Modifier.height(50.dp).fillMaxWidth(), horizontalArrangement = Arrangement.End) { + CircularButton(painterResource("list_view.png")) { onToggle() } } }) } diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt index 6603ac9b78..3a342d6237 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/MemoryScreen.kt @@ -1,6 +1,8 @@ package example.imageviewer.view import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* @@ -9,6 +11,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar @@ -22,9 +25,12 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -49,38 +55,17 @@ internal fun MemoryScreen( ) { val pictures by photoGallery.galleryStateFlow.collectAsState() val picture = pictures.first { it.id == memoryPage.galleryId } - Column { - TopAppBar( - modifier = Modifier.background(brush = ImageviewerColors.kotlinHorizontalGradientBrush), - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = ImageviewerColors.Transparent, - titleContentColor = MaterialTheme.colorScheme.onBackground - ), - title = { - Text("") - }, - navigationIcon = { - Tooltip("Back") { - Image( - painterResource("back.png"), - contentDescription = null, - modifier = Modifier.size(38.dp) - .clip(CircleShape) - .clickable { onBack() } - ) - } - }, - ) + Box { val scrollState = memoryPage.scrollState Column( modifier = Modifier .fillMaxWidth() - .verticalScroll(scrollState), + .verticalScroll(scrollState) ) { Box( modifier = Modifier .fillMaxWidth() - .height(300.dp) + .height(393.dp) .background(Color.White) .graphicsLayer { translationY = 0.5f * scrollState.value @@ -89,11 +74,11 @@ internal fun MemoryScreen( ) { MemoryHeader(picture.thumbnail, onClick = { onHeaderClick(memoryPage.galleryId) }) } - Box(modifier = Modifier.background(ImageviewerColors.kotlinHorizontalGradientBrush)) { + Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { Column { - Headliner("Where it happened") + Headliner("Place") LocationVisualizer() - Headliner("What happened") + Headliner("Note") Collapsible( """ I took a picture with my iPhone 14 at 17:45. The picture ended up being 3024 x 4032 pixels. ✨ @@ -106,22 +91,74 @@ internal fun MemoryScreen( Headliner("Related memories") RelatedMemoriesVisualizer(pictures, onSelectRelatedMemory) Spacer(Modifier.height(50.dp)) - Text( - "Delete this memory", - Modifier.fillMaxWidth(), - textAlign = TextAlign.Center, - color = Color.White - ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy( + 8.dp, + Alignment.CenterHorizontally + ), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painterResource("trash.png"), + contentDescription = null, + modifier = Modifier.size(14.dp) + ) + Text( + text = "Delete Memory", + textAlign = TextAlign.Left, + color = ImageviewerColors.onBackground, + fontSize = 14.sp, + fontWeight = FontWeight.Normal + ) + } Spacer(Modifier.height(50.dp)) } } } + TopAppBar( + modifier = Modifier.padding(start = 12.dp, end = 12.dp), + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = ImageviewerColors.Transparent, + titleContentColor = MaterialTheme.colorScheme.onBackground + ), + title = { + Text("") + }, + navigationIcon = { + Tooltip("Back") { + CircularButton(painterResource("arrowleft.png"), onClick = { onBack() }) + } + }, + ) } } +@Composable +internal fun CircularButton(image: Painter, onClick: () -> Unit) { + Box( + Modifier.size(40.dp).clip(CircleShape).background(ImageviewerColors.uiLightBlack) + .clickable { onClick() }, contentAlignment = Alignment.Center + ) { + Image( + image, + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + } + +} + @Composable private fun MemoryHeader(bitmap: ImageBitmap, onClick: () -> Unit) { val interactionSource = remember { MutableInteractionSource() } + val shadowTextStyle = LocalTextStyle.current.copy( + shadow = Shadow( + color = Color.Black, + offset = Offset(4f, 4f), + blurRadius = 4f + ) + ) Box(modifier = Modifier.clickable(interactionSource, null, onClick = { onClick() })) { Image( bitmap, @@ -129,35 +166,27 @@ private fun MemoryHeader(bitmap: ImageBitmap, onClick: () -> Unit) { contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize() ) - Column(modifier = Modifier.align(Alignment.Center)) { + Column( + modifier = Modifier.align(Alignment.BottomStart).padding(start = 12.dp, bottom = 16.dp) + ) { Text( "Your Memory", - textAlign = TextAlign.Center, + textAlign = TextAlign.Left, color = Color.White, - fontSize = 50.sp, + fontSize = 20.sp, modifier = Modifier.fillMaxWidth(), - fontWeight = FontWeight.Black + fontWeight = FontWeight.SemiBold, + style = shadowTextStyle + ) + Spacer(Modifier.height(5.dp)) + + Text( + "19th of April 2023", + textAlign = TextAlign.Left, + color = Color.White, + fontWeight = FontWeight.Normal, + style = shadowTextStyle ) - Spacer(Modifier.height(30.dp)) - Box( - modifier = Modifier - .align(Alignment.CenterHorizontally) - .border( - width = 2.dp, - color = Color.Black, - shape = RoundedCornerShape(100.dp) - ) - .clip( - RoundedCornerShape(100.dp) - ) - .background(Color.Black.copy(alpha = 0.7f)).padding(10.dp) - ) { - Text( - "19th of April 2023", - textAlign = TextAlign.Center, - color = Color.White - ) - } } } } @@ -169,12 +198,18 @@ internal fun Collapsible(s: String) { val text = if (isCollapsed) s.lines().first() + "... (see more)" else s Text( text, + fontSize = 12.sp, modifier = Modifier .padding(10.dp, 0.dp) .clip(RoundedCornerShape(10.dp)) - .background(Color.White) + .background(ImageviewerColors.noteBlockBackground) .padding(10.dp) - .animateContentSize() + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) .clickable(interactionSource = interctionSource, indication = null) { isCollapsed = !isCollapsed }, @@ -185,10 +220,10 @@ internal fun Collapsible(s: String) { internal fun Headliner(s: String) { Text( text = s, - fontWeight = FontWeight.Bold, - fontSize = 32.sp, - color = Color.White, - modifier = Modifier.padding(10.dp, 30.dp, 10.dp, 10.dp) + fontWeight = FontWeight.SemiBold, + fontSize = 20.sp, + color = Color.Black, + modifier = Modifier.padding(start = 12.dp, top = 32.dp, end = 12.dp, bottom = 16.dp) ) } @@ -212,7 +247,10 @@ internal fun RelatedMemoriesVisualizer( modifier = Modifier.padding(10.dp, 0.dp).clip(RoundedCornerShape(10.dp)).fillMaxWidth() .height(200.dp) ) { - LazyRow(modifier = Modifier.fillMaxSize()) { + LazyRow( + modifier = Modifier.fillMaxSize(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { itemsIndexed(ps) { idx, item -> RelatedMemory(idx, item, onSelectRelatedMemory) } @@ -226,8 +264,10 @@ internal fun RelatedMemory( galleryEntry: GalleryEntryWithMetadata, onSelectRelatedMemory: (GalleryId) -> Unit ) { - SquareMiniature( - galleryEntry.thumbnail, - false, - onClick = { onSelectRelatedMemory(galleryEntry.id) }) + Box(Modifier.size(130.dp).clip(RoundedCornerShape(8.dp))) { + SquareMiniature( + galleryEntry.thumbnail, + false, + onClick = { onSelectRelatedMemory(galleryEntry.id) }) + } } \ No newline at end of file diff --git a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt index 995ffa86d9..07ab3c85b0 100644 --- a/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt +++ b/experimental/examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/view/PreviewImage.common.kt @@ -2,31 +2,30 @@ package example.imageviewer.view import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.with import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Card import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue 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 import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import example.imageviewer.model.Picture -import example.imageviewer.style.ImageviewerColors.kotlinHorizontalGradientBrush @OptIn(ExperimentalAnimationApi::class) @Composable @@ -41,41 +40,47 @@ internal fun PreviewImage( image = getImage(picture) } } + Box(Modifier.fillMaxWidth().height(393.dp), contentAlignment = Alignment.Center) { + Box( + modifier = Modifier.fillMaxSize() + .clickable { onClick() }, + ) { + AnimatedContent( + targetState = image, + transitionSpec = { + slideInHorizontally( + initialOffsetX = { it }, animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) with slideOutHorizontally( + targetOffsetX = { -it }, animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) +// slideInVertically(initialOffsetY = { it }) with slideOutVertically(targetOffsetY = { -it }) + } + ) { imageBitmap -> + if (imageBitmap != null) { + Image( + bitmap = imageBitmap, + contentDescription = null, + modifier = Modifier + .fillMaxSize(), + contentScale = ContentScale.Crop + ) + } else { + Spacer( + modifier = Modifier.fillMaxSize() - Spacer( - modifier = Modifier.height(5.dp).fillMaxWidth() - .background(brush = kotlinHorizontalGradientBrush) - ) - Card( - modifier = Modifier.height(200.dp) - .background(brush = kotlinHorizontalGradientBrush) - .padding(10.dp) - .clickable { onClick() }, - shape = RoundedCornerShape(10.dp, 10.dp, 10.dp, 10.dp), - ) { - AnimatedContent( - targetState = image, - transitionSpec = { - slideInVertically(initialOffsetY = { it }) with slideOutVertically(targetOffsetY = { -it }) - } - ) { imageBitmap -> - if (imageBitmap != null) { - Image( - bitmap = imageBitmap, - contentDescription = null, - modifier = Modifier - .fillMaxSize() - , - contentScale = ContentScale.Crop - ) - } else { - Spacer( - modifier = Modifier.fillMaxSize() - .background(brush = kotlinHorizontalGradientBrush) - ) + ) + } } } } + + } @Composable diff --git a/experimental/examples/imageviewer/shared/src/commonMain/resources/arrowleft.png b/experimental/examples/imageviewer/shared/src/commonMain/resources/arrowleft.png new file mode 100644 index 0000000000..4554dc244d Binary files /dev/null and b/experimental/examples/imageviewer/shared/src/commonMain/resources/arrowleft.png differ diff --git a/experimental/examples/imageviewer/shared/src/commonMain/resources/eye.png b/experimental/examples/imageviewer/shared/src/commonMain/resources/eye.png new file mode 100644 index 0000000000..53503d3d7f Binary files /dev/null and b/experimental/examples/imageviewer/shared/src/commonMain/resources/eye.png differ diff --git a/experimental/examples/imageviewer/shared/src/commonMain/resources/plus.png b/experimental/examples/imageviewer/shared/src/commonMain/resources/plus.png new file mode 100644 index 0000000000..5a46adbc81 Binary files /dev/null and b/experimental/examples/imageviewer/shared/src/commonMain/resources/plus.png differ diff --git a/experimental/examples/imageviewer/shared/src/commonMain/resources/trash.png b/experimental/examples/imageviewer/shared/src/commonMain/resources/trash.png new file mode 100644 index 0000000000..67e863f04a Binary files /dev/null and b/experimental/examples/imageviewer/shared/src/commonMain/resources/trash.png differ