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

Custom richtext spans #184

Merged
merged 3 commits into from
Feb 2, 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
Expand Up @@ -2,21 +2,19 @@ package com.mohamedrejeb.richeditor.model

import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
import com.mohamedrejeb.richeditor.utils.fastForEachIndexed
import com.mohamedrejeb.richeditor.utils.getBoundingBoxes

internal interface RichSpanStyle {
interface RichSpanStyle {
val spanStyle: (RichTextConfig) -> SpanStyle
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lambda can be changed to a function called getSpanStyle or something like that. It's more readable.


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.mohamedrejeb.richeditor.model
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextDecoration

internal data class RichTextConfig(
data class RichTextConfig(
val linkColor: Color = Color.Blue,
val linkTextDecoration: TextDecoration = TextDecoration.Underline,
val codeColor: Color = Color.Unspecified,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,26 @@ class RichTextState internal constructor(
handleAddingStyleToSelectedText()
}

//RichSpanStyle

fun addRichSpan(spanStyle: RichSpanStyle) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method can be reused by add/remove codeSpan methods.

if (toRemoveRichSpanStyle::class == spanStyle)
toRemoveRichSpanStyle = RichSpanStyle.Default
toAddRichSpanStyle = spanStyle

if (!selection.collapsed)
handleAddingStyleToSelectedText()
}

fun removeRichSpan(spanStyle: RichSpanStyle) {
if (toAddRichSpanStyle::class == spanStyle::class)
toAddRichSpanStyle = RichSpanStyle.Default
toRemoveRichSpanStyle = spanStyle

if (!selection.collapsed)
handleAddingStyleToSelectedText()
}

/**
* Toggle the [ParagraphStyle]
* If the passed paragraph style doesn't exist in the [currentParagraphStyle] it's going to be added.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.mohamedrejeb.richeditor.model
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp

internal data class TextPaddingValues(
data class TextPaddingValues(
val horizontal: TextUnit = 0.sp,
val vertical: TextUnit = 0.sp
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
}

@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEachIndexed(action: (index: Int, T) -> Unit) {
inline fun <T> List<T>.fastForEachIndexed(action: (index: Int, T) -> Unit) {
contract { callsInPlace(action) }
for (index in indices) {
val item = get(index)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import androidx.compose.ui.text.style.ResolvedTextDirection
* entire paragraphs is returned instead of separate lines if [startOffset]
* and [endOffset] represent the extreme ends of those paragraph.
*/
internal fun TextLayoutResult.getBoundingBoxes(
fun TextLayoutResult.getBoundingBoxes(
startOffset: Int,
endOffset: Int,
flattenForFullParagraphs: Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.mohamedrejeb.richeditor.model.RichTextState
import com.mohamedrejeb.richeditor.sample.common.richeditor.SpellCheck

@Composable
fun RichTextStyleRow(
Expand Down Expand Up @@ -202,7 +203,15 @@ fun RichTextStyleRow(
)
}


item {
RichTextStyleButton(
onClick = {
state.addRichSpan(SpellCheck)
},
isSelected = false,
icon = Icons.Outlined.Spellcheck,
)
}

item {
Box(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.mohamedrejeb.richeditor.sample.common.richeditor

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.unit.dp
import com.mohamedrejeb.richeditor.model.RichSpanStyle
import com.mohamedrejeb.richeditor.model.RichTextConfig
import com.mohamedrejeb.richeditor.utils.fastForEachIndexed
import com.mohamedrejeb.richeditor.utils.getBoundingBoxes

object SpellCheck: RichSpanStyle {
override val spanStyle: (RichTextConfig) -> SpanStyle = {
SpanStyle()
}

override fun DrawScope.drawCustomStyle(
layoutResult: TextLayoutResult,
textRange: TextRange,
richTextConfig: RichTextConfig,
topPadding: Float,
startPadding: Float,
) {
val path = Path()
val strokeColor = Color.Red
val boxes = layoutResult.getBoundingBoxes(
startOffset = textRange.start,
endOffset = textRange.end,
flattenForFullParagraphs = true,
)

boxes.fastForEachIndexed { index, box ->
path.moveTo(box.left + startPadding, box.bottom + topPadding)
path.lineTo(box.right + startPadding, box.bottom + topPadding)

drawPath(
path = path,
color = strokeColor,
style = Stroke(
width = 2.dp.toPx(),
)
)
}
}

override val acceptNewTextInTheEdges: Boolean = false
}
Loading