Skip to content

Commit

Permalink
Merge pull request #305 from MohamedRejeb/1.x
Browse files Browse the repository at this point in the history
Add Image support to RichText
  • Loading branch information
MohamedRejeb authored Jul 20, 2024
2 parents eccef2b + e357a81 commit 76373ac
Showing 26 changed files with 1,376 additions and 720 deletions.
18 changes: 17 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -6,14 +6,17 @@ dokka = "1.9.10"

ksoup = "0.3.2"
jetbrainsMarkdown = "0.7.0"
coil = "3.0.0-alpha06"

nexus-publish = "2.0.0-rc-1"
nexus-publish = "2.0.0"

# For sample
compose-compiler = "1.5.14"
activity-compose = "1.9.0"
voyager = "1.1.0-alpha04"
richeditor = "1.0.0-rc04"
coroutines = "1.8.1"
ktor = "3.0.0-wasm2"
android-minSdk = "21"
android-compileSdk = "34"

@@ -22,13 +25,26 @@ ksoup-html = { module = "com.mohamedrejeb.ksoup:ksoup-html", version.ref = "ksou
ksoup-entities = { module = "com.mohamedrejeb.ksoup:ksoup-entities", version.ref = "ksoup" }
jetbrains-markdown = { module = "org.jetbrains:markdown", version.ref = "jetbrainsMarkdown" }

coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }

nexus-publish = { module = "io.github.gradle-nexus.publish-plugin:io.github.gradle-nexus.publish-plugin.gradle.plugin", version.ref = "nexus-publish" }

# For sample
activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
richeditor-compose = { module = "com.mohamedrejeb.richeditor:richeditor-compose", version.ref = "richeditor" }

kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }

coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil" }
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor", version.ref = "coil" }

ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" }

[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }
androidApplication = { id = "com.android.application", version.ref = "agp" }
Empty file.
999 changes: 548 additions & 451 deletions kotlin-js-store/yarn.lock

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions richeditor-compose-coil3/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl

plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.androidLibrary)
id("module.publication")
}

kotlin {
applyDefaultHierarchyTemplate()
androidTarget {
publishLibraryVariants("release")
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
jvm("desktop") {
jvmToolchain(11)
}
js(IR) {
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser {
testTask {
enabled = false
}
}
}
iosX64()
iosArm64()
iosSimulatorArm64()

sourceSets.commonMain.dependencies {
implementation(projects.richeditorCompose)

implementation(compose.ui)
implementation(compose.foundation)

implementation(libs.coil.compose)
}

sourceSets.commonTest.dependencies {
implementation(kotlin("test"))
}
}

android {
namespace = "com.mohamedrejeb.richeditor.compose.coil"
compileSdk = libs.versions.android.compileSdk.get().toInt()

defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlin {
jvmToolchain(8)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.mohamedrejeb.richeditor.coil3

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import coil3.compose.AsyncImagePainter
import coil3.compose.rememberAsyncImagePainter
import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi
import com.mohamedrejeb.richeditor.model.ImageData
import com.mohamedrejeb.richeditor.model.ImageLoader

@OptIn(ExperimentalRichTextApi::class)
object Coil3ImageLoader: ImageLoader {

@Composable
override fun load(model: Any): ImageData {
val painter = rememberAsyncImagePainter(model = model)

return ImageData(
painter = painter
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.mohamedrejeb.richeditor.model

import androidx.compose.runtime.Composable
import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi

@ExperimentalRichTextApi
object DefaultImageLoader: ImageLoader {

@Composable
override fun load(model: Any): ImageData? = null

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.mohamedrejeb.richeditor.model

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.Density
import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi

@ExperimentalRichTextApi
interface ImageLoader {

@Composable
fun load(model: Any): ImageData?

}

@ExperimentalRichTextApi
val LocalImageLoader = staticCompositionLocalOf<ImageLoader> {
DefaultImageLoader
}

@Immutable
data class ImageData(
val painter: Painter,
val contentDescription: String? = "Image",
val alignment: Alignment = Alignment.CenterStart,
val modifier: Modifier = Modifier.fillMaxWidth()
)
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ internal class RichSpan(
var text: String = "",
var textRange: TextRange = TextRange(start = 0, end = 0),
var spanStyle: SpanStyle = SpanStyle(),
var richSpansStyle: RichSpanStyle = RichSpanStyle.Default,
var richSpanStyle: RichSpanStyle = RichSpanStyle.Default,
) {
/**
* Return the full text range of the rich span.
@@ -61,11 +61,11 @@ internal class RichSpan(
}

val fullStyle: RichSpanStyle get() {
var style = this.richSpansStyle
var style = this.richSpanStyle
var parent = this.parent

while (parent != null && style::class == RichSpanStyle.Default::class) {
style = parent.richSpansStyle
style = parent.richSpanStyle
parent = parent.parent
}

@@ -180,7 +180,7 @@ internal class RichSpan(
*
* @return True if the rich span is blank, false otherwise
*/
fun isBlank(): Boolean = text.isBlank() && isChildrenBlank()
fun isBlank(): Boolean = text.isBlank() && isChildrenBlank() && richSpanStyle !is RichSpanStyle.Image

/**
* Check if the rich span children are empty
@@ -189,7 +189,7 @@ internal class RichSpan(
*/
private fun isChildrenEmpty(): Boolean =
children.all { richSpan ->
richSpan.text.isEmpty() && richSpan.isChildrenEmpty()
richSpan.isEmpty()
}

/**
@@ -199,7 +199,7 @@ internal class RichSpan(
*/
private fun isChildrenBlank(): Boolean =
children.all { richSpan ->
richSpan.text.isBlank() && richSpan.isChildrenBlank()
richSpan.isBlank()
}

/**
@@ -267,7 +267,7 @@ internal class RichSpan(
// Set start text range
textRange = TextRange(start = index, end = index + text.length)

if (!richSpansStyle.acceptNewTextInTheEdges && !ignoreCustomFiltering) {
if (!richSpanStyle.acceptNewTextInTheEdges && !ignoreCustomFiltering) {
val fullTextRange = fullTextRange
if (textIndex == fullTextRange.max - 1) {
index += fullTextRange.length
@@ -423,7 +423,7 @@ internal class RichSpan(
fun getClosestRichSpan(spanStyle: SpanStyle, newRichSpanStyle: RichSpanStyle): RichSpan? {
if (
spanStyle.isSpecifiedFieldsEquals(this.fullSpanStyle, strict = true) &&
newRichSpanStyle::class == richSpansStyle::class
newRichSpanStyle::class == richSpanStyle::class
) return this

return parent?.getClosestRichSpan(spanStyle, newRichSpanStyle)
@@ -448,7 +448,7 @@ internal class RichSpan(
paragraph = newParagraph,
text = text,
textRange = textRange,
richSpansStyle = richSpansStyle,
richSpanStyle = richSpanStyle,
spanStyle = spanStyle,
)
children.fastForEach { childRichSpan ->
@@ -459,6 +459,26 @@ internal class RichSpan(
return newSpan
}

internal fun copy(
key: Int? = this.key,
children: MutableList<RichSpan> = this.children,
paragraph: RichParagraph = this.paragraph,
parent: RichSpan? = this.parent,
text: String = this.text,
textRange: TextRange = this.textRange,
spanStyle: SpanStyle = this.spanStyle,
richSpanStyle: RichSpanStyle = this.richSpanStyle,
) = RichSpan(
key = key,
children = children,
paragraph = paragraph,
parent = parent,
text = text,
textRange = textRange,
spanStyle = spanStyle,
richSpanStyle = richSpanStyle,
)

override fun toString(): String {
return "richSpan(text='$text', textRange=$textRange, fullTextRange=$fullTextRange)"
}
Loading

0 comments on commit 76373ac

Please sign in to comment.