diff --git a/gradle.properties b/gradle.properties index 54da5ace..9955f851 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,18 +1,16 @@ #Gradle org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" +org.gradle.caching=true +org.gradle.parallel=true +#Kotlin kotlin.code.style=official -#MPP -kotlin.mpp.androidSourceSetLayoutVersion=2 -kotlin.mpp.enableCInteropCommonization=true - #Android android.useAndroidX=true android.nonTransitiveRClass=true #Web -kotlin.js.compiler=ir org.jetbrains.compose.experimental.jscanvas.enabled=true #iOS diff --git a/richeditor-compose/src/androidMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.android.kt b/richeditor-compose/src/androidMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.android.kt new file mode 100644 index 00000000..02897b34 --- /dev/null +++ b/richeditor-compose/src/androidMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.android.kt @@ -0,0 +1,3 @@ +package com.mohamedrejeb.richeditor.platform + +internal actual val currentPlatform: Platform = Platform.Android \ No newline at end of file diff --git a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/model/RichTextState.kt b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/model/RichTextState.kt index e5b2ef49..86e273f7 100644 --- a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/model/RichTextState.kt +++ b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/model/RichTextState.kt @@ -20,12 +20,17 @@ import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi import com.mohamedrejeb.richeditor.model.RichParagraph.Type.Companion.startText import com.mohamedrejeb.richeditor.parser.html.RichTextStateHtmlParser import com.mohamedrejeb.richeditor.parser.markdown.RichTextStateMarkdownParser +import com.mohamedrejeb.richeditor.platform.currentPlatform import com.mohamedrejeb.richeditor.utils.* import com.mohamedrejeb.richeditor.utils.append import com.mohamedrejeb.richeditor.utils.customMerge import com.mohamedrejeb.richeditor.utils.isSpecifiedFieldsEquals import com.mohamedrejeb.richeditor.utils.unmerge +import kotlinx.coroutines.Job +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import kotlin.math.max @Composable @@ -506,6 +511,21 @@ class RichTextState internal constructor( updateParagraphType(paragraph, RichParagraph.Type.Default) } + private val textFieldValueSharedFlow = MutableSharedFlow() + + internal suspend fun emitTextFieldValue(textFieldValue: TextFieldValue) { + textFieldValueSharedFlow.emit(textFieldValue) + } + + internal var collectTextFieldValueSharedFlowJob: Job? = null + internal suspend fun collectTextFieldValueSharedFlow() = coroutineScope { + collectTextFieldValueSharedFlowJob = launch { + textFieldValueSharedFlow.collect { newTextFieldValue -> + onTextFieldValueChange(newTextFieldValue) + } + } + } + /** * Temporarily stores the new text field value, before it is validated. */ @@ -517,6 +537,11 @@ class RichTextState internal constructor( * @param newTextFieldValue the new text field value. */ internal fun onTextFieldValueChange(newTextFieldValue: TextFieldValue) { + println("onTextFieldValueChange: ${newTextFieldValue.text.replace("\n", "
")}") + println("same text field value: ${newTextFieldValue == tempTextFieldValue}") + println("same text: ${newTextFieldValue.text == tempTextFieldValue.text}") + println("same selection: ${newTextFieldValue.selection == tempTextFieldValue.selection}") + if (newTextFieldValue == tempTextFieldValue) return tempTextFieldValue = newTextFieldValue if (tempTextFieldValue.text.length > textFieldValue.text.length) @@ -576,6 +601,12 @@ class RichTextState internal constructor( // Clear [tempTextFieldValue] tempTextFieldValue = TextFieldValue() + + println("end edit") + richParagraphList.forEachIndexed { index, paragraph -> + println("Paragraph: $index") + println(paragraph.toString()) + } } /** @@ -628,7 +659,12 @@ class RichTextState internal constructor( // Add empty space to the last paragraph if it's empty. // Workaround to fix an issue with Compose TextField that causes a crash on long click - if (i > 0 && i == richParagraphList.lastIndex && richParagraph.isEmpty()) { + if ( + (currentPlatform.isAndroid || + currentPlatform.isIOS) && + i > 0 && i == richParagraphList.lastIndex && + richParagraph.isEmpty() + ) { richParagraph.getFirstNonEmptyChild()?.text = " " append(" ") index++ @@ -1027,6 +1063,10 @@ class RichTextState internal constructor( val sliceIndex = max(index, richSpan.textRange.min) // Create a new paragraph style + + println("oldParagraphType: ${richSpan.paragraph.type}") + println("oldParagraphStartText: ${richSpan.paragraph.type.startText}") + val newParagraph = richSpan.paragraph.slice( startIndex = sliceIndex, richSpan = richSpan, @@ -1050,6 +1090,10 @@ class RichTextState internal constructor( // Update the paragraph type of the paragraphs after the new paragraph val newParagraphType = newParagraph.type + + println("newParagraphType: $newParagraphType") + println("newParagraphStartText: ${newParagraphType.startText}") + if (newParagraphType is RichParagraph.Type.OrderedList) { tempTextFieldValue = adjustOrderedListsNumbers( startParagraphIndex = paragraphIndex + 2, diff --git a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.kt b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.kt new file mode 100644 index 00000000..048865f9 --- /dev/null +++ b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.kt @@ -0,0 +1,19 @@ +package com.mohamedrejeb.richeditor.platform + +internal enum class Platform { + Android, IOS, Desktop, Web; + + val isAndroid: Boolean + get() = this == Android + + val isIOS: Boolean + get() = this == IOS + + val isDesktop: Boolean + get() = this == Desktop + + val isWeb: Boolean + get() = this == Web +} + +internal expect val currentPlatform: Platform \ No newline at end of file diff --git a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/ui/BasicRichTextEditor.kt b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/ui/BasicRichTextEditor.kt index 2c36628c..04332b33 100644 --- a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/ui/BasicRichTextEditor.kt +++ b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/ui/BasicRichTextEditor.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.sp import com.mohamedrejeb.richeditor.model.RichParagraph import com.mohamedrejeb.richeditor.model.RichTextState import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch /** @@ -197,6 +198,7 @@ internal fun BasicRichTextEditor( @Composable { innerTextField -> innerTextField() }, contentPadding: PaddingValues ) { + val scope = rememberCoroutineScope() val density = LocalDensity.current val localTextStyle = LocalTextStyle.current val layoutDirection = LocalLayoutDirection.current @@ -233,12 +235,25 @@ internal fun BasicRichTextEditor( } } + DisposableEffect(state) { + scope.launch { + state.collectTextFieldValueSharedFlow() + } + + onDispose { + state.collectTextFieldValueSharedFlowJob?.cancel() + } + } + CompositionLocalProvider(LocalClipboardManager provides richClipboardManager) { BasicTextField( value = state.textFieldValue, onValueChange = { if (readOnly) return@BasicTextField if (it.text.length > maxLength) return@BasicTextField +// scope.launch { +// state.emitTextFieldValue(it) +// } state.onTextFieldValueChange(it) }, modifier = modifier diff --git a/richeditor-compose/src/desktopMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.desktop.kt b/richeditor-compose/src/desktopMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.desktop.kt new file mode 100644 index 00000000..d72f80f1 --- /dev/null +++ b/richeditor-compose/src/desktopMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.desktop.kt @@ -0,0 +1,3 @@ +package com.mohamedrejeb.richeditor.platform + +internal actual val currentPlatform: Platform = Platform.Desktop \ No newline at end of file diff --git a/richeditor-compose/src/iosMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.ios.kt b/richeditor-compose/src/iosMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.ios.kt new file mode 100644 index 00000000..84d3bc3b --- /dev/null +++ b/richeditor-compose/src/iosMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.ios.kt @@ -0,0 +1,3 @@ +package com.mohamedrejeb.richeditor.platform + +internal actual val currentPlatform: Platform = Platform.IOS \ No newline at end of file diff --git a/richeditor-compose/src/jsMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.js.kt b/richeditor-compose/src/jsMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.js.kt new file mode 100644 index 00000000..d86dc2e6 --- /dev/null +++ b/richeditor-compose/src/jsMain/kotlin/com/mohamedrejeb/richeditor/platform/Platform.js.kt @@ -0,0 +1,3 @@ +package com.mohamedrejeb.richeditor.platform + +internal actual val currentPlatform: Platform = Platform.Web \ No newline at end of file diff --git a/sample/ios/Compose Rich Editor.xcodeproj/project.xcworkspace/xcuserdata/mohamedbenrejeb.xcuserdatad/UserInterfaceState.xcuserstate b/sample/ios/Compose Rich Editor.xcodeproj/project.xcworkspace/xcuserdata/mohamedbenrejeb.xcuserdatad/UserInterfaceState.xcuserstate index e7c2f3db..e1f98257 100644 Binary files a/sample/ios/Compose Rich Editor.xcodeproj/project.xcworkspace/xcuserdata/mohamedbenrejeb.xcuserdatad/UserInterfaceState.xcuserstate and b/sample/ios/Compose Rich Editor.xcodeproj/project.xcworkspace/xcuserdata/mohamedbenrejeb.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/sample/web/src/jsMain/kotlin/Main.kt b/sample/web/src/jsMain/kotlin/Main.kt index 5eb35a73..f4c1b09b 100644 --- a/sample/web/src/jsMain/kotlin/Main.kt +++ b/sample/web/src/jsMain/kotlin/Main.kt @@ -1,13 +1,17 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.window.Window +import androidx.compose.ui.window.CanvasBasedWindow import com.mohamedrejeb.richeditor.sample.common.App import org.jetbrains.skiko.wasm.onWasmReady +@OptIn(ExperimentalComposeUiApi::class) fun main() { onWasmReady { - Window("Compose Rich Editor") { + CanvasBasedWindow( + title = "Compose Rich Editor" + ) { Box(Modifier.fillMaxSize()) { App() } diff --git a/sample/web/src/jsMain/resources/index.html b/sample/web/src/jsMain/resources/index.html index d34c6fd0..0fdcc47a 100644 --- a/sample/web/src/jsMain/resources/index.html +++ b/sample/web/src/jsMain/resources/index.html @@ -3,22 +3,17 @@ Compose Rich Editor - + - - - + +
+
+
+ +
\ No newline at end of file diff --git a/sample/web/webpack.config.d/fs.js b/sample/web/webpack.config.d/fs.js deleted file mode 100644 index e69de29b..00000000