Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor MarkdownImage and InlineImage handling #204

Merged
merged 3 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package com.mikepenz.markdown.coil3

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.painter.Painter
import coil3.compose.AsyncImagePainter
Expand All @@ -19,7 +13,7 @@ import com.mikepenz.markdown.model.ImageTransformer
object Coil3ImageTransformerImpl : ImageTransformer {

@Composable
override fun transform(link: String): ImageData? {
override fun transform(link: String): ImageData {
return rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalPlatformContext.current)
.data(link)
Expand All @@ -33,13 +27,9 @@ object Coil3ImageTransformerImpl : ImageTransformer {
var size by remember(painter) { mutableStateOf(painter.intrinsicSize) }
if (painter is AsyncImagePainter) {
val painterState = painter.state.collectAsState()
LaunchedEffect(painterState) {
painterState.value.painter?.let {
size = it.intrinsicSize
}
}
val intrinsicSize = painterState.value.painter?.intrinsicSize
intrinsicSize?.also { size = it }
}

return size
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ fun MarkdownImage(content: String, node: ASTNode) {
Image(
painter = imageData.painter,
contentDescription = imageData.contentDescription,
modifier = imageData.modifier,
alignment = imageData.alignment,
modifier = imageData.modifier
contentScale = imageData.contentScale,
alpha = imageData.alpha,
colorFilter = imageData.colorFilter
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.*
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp
import com.mikepenz.markdown.compose.*
import com.mikepenz.markdown.compose.elements.material.MarkdownBasicText
Expand Down Expand Up @@ -110,6 +110,16 @@ fun MarkdownText(
}
} else modifier


val transformer = LocalImageTransformer.current
val placeholderState by derivedStateOf {
transformer.placeholderConfig(
imageState.density,
imageState.containerSize,
imageState.intrinsicImageSize
)
}

MarkdownBasicText(
text = content,
modifier = textModifier
Expand All @@ -123,31 +133,30 @@ fun MarkdownText(
color = LocalMarkdownColors.current.text,
inlineContent = mapOf(MARKDOWN_TAG_IMAGE_URL to InlineTextContent(
Placeholder(
width = imageState.imageSize.width.sp,
height = imageState.imageSize.height.sp,
placeholderVerticalAlign = PlaceholderVerticalAlign.Bottom
width = placeholderState.size.width.sp,
height = placeholderState.size.height.sp,
placeholderVerticalAlign = placeholderState.verticalAlign
)
) { link ->
val transformer = LocalImageTransformer.current

transformer.transform(link)?.let { imageData ->
val intrinsicSize = transformer.intrinsicSize(imageData.painter)

LaunchedEffect(intrinsicSize) {
imageState.setImageSize(intrinsicSize)
}

Image(
painter = imageData.painter,
contentDescription = imageData.contentDescription,
modifier = imageData.modifier,
alignment = imageData.alignment,
modifier = imageData.modifier
contentScale = imageData.contentScale,
alpha = imageData.alpha,
colorFilter = imageData.colorFilter
)
}
}),
onTextLayout = {
layoutResult.value = it
onTextLayout?.invoke(it)
onTextLayout.invoke(it)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,64 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.unit.Density

interface ImageTransformer {
/**
* Will retrieve the [ImageData] from an image link/url
*/
@Composable
fun transform(link: String): ImageData?

/**
* Returns the detected intrinsic size of the painter
*/
@Composable
fun intrinsicSize(painter: Painter): Size {
return painter.intrinsicSize
}

/**
* The expected placeholderSize. Note: The same size is shared for all inline images within a single MarkdownText item.
*/
fun placeholderConfig(density: Density, containerSize: Size, intrinsicImageSize: Size): PlaceholderConfig {
return PlaceholderConfig(with(density) {
if (containerSize.isUnspecified) {
Size(180f, 180f)
} else if (intrinsicImageSize.isUnspecified) {
Size(containerSize.width.toSp().value, 180f)
} else {
val width = minOf(intrinsicImageSize.width, containerSize.width)
val height = if (intrinsicImageSize.width < containerSize.width) {
intrinsicImageSize.height
} else {
(intrinsicImageSize.height * containerSize.width) / intrinsicImageSize.width
}
Size(width.toSp().value, height.toSp().value)
}
})
}
}

@Immutable
data class PlaceholderConfig(
val size: Size,
val verticalAlign: PlaceholderVerticalAlign = PlaceholderVerticalAlign.Bottom,
)

@Immutable
data class ImageData(
val painter: Painter,
val modifier: Modifier = Modifier.fillMaxWidth(),
val contentDescription: String? = "Image",
val alignment: Alignment = Alignment.CenterStart,
val modifier: Modifier = Modifier.fillMaxWidth()
val contentScale: ContentScale = ContentScale.Fit,
val alpha: Float = DefaultAlpha,
val colorFilter: ColorFilter? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,27 @@ package com.mikepenz.markdown.model

import androidx.compose.runtime.*
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.toSize

internal interface MarkdownImageState {
val imageSize: Size
val density: Density
val containerSize: Size
val intrinsicImageSize: Size
fun setContainerSize(intSize: IntSize)
fun setImageSize(size: Size)
}

internal class MarkdownImageStateImpl(private val density: Density) : MarkdownImageState {

private var parentSize by mutableStateOf(Size.Unspecified)

private var intrinsicImageSize by mutableStateOf(Size.Unspecified)

override val imageSize by derivedStateOf {
with(density) {
if (parentSize.isUnspecified) {
Size(180f, 180f)
} else if (intrinsicImageSize.isUnspecified) {
Size(parentSize.width.toSp().value, 180f)
} else {
val width = minOf(intrinsicImageSize.width, parentSize.width)

val height = if (intrinsicImageSize.width < parentSize.width) {
intrinsicImageSize.height
} else {
(intrinsicImageSize.height * parentSize.width) / intrinsicImageSize.width
}
Size(width.toSp().value, height.toSp().value)
}
}
}
internal class MarkdownImageStateImpl(override val density: Density) : MarkdownImageState {

override var containerSize by mutableStateOf(Size.Unspecified)

override var intrinsicImageSize by mutableStateOf(Size.Unspecified)

override fun setContainerSize(intSize: IntSize) {
parentSize = intSize.toSize()
containerSize = intSize.toSize()
}

override fun setImageSize(size: Size) {
Expand Down