Skip to content
Open
Changes from 3 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 @@ -4,12 +4,72 @@ import android.text.Editable
import android.text.Spannable
import android.text.SpannableStringBuilder
import com.swmansion.enriched.EnrichedTextInputView
import com.swmansion.enriched.spans.EnrichedBlockQuoteSpan
import com.swmansion.enriched.spans.EnrichedCodeBlockSpan
import com.swmansion.enriched.spans.EnrichedSpans
import com.swmansion.enriched.utils.getParagraphBounds
import com.swmansion.enriched.utils.getSafeSpanBoundaries

class ParagraphStyles(private val view: EnrichedTextInputView) {
private fun <T>getPreviousParagraphSpan(spannable: Spannable, paragraphStart: Int, type: Class<T>): T? {
if (paragraphStart <= 0) return null

val (previousParagraphStart, previousParagraphEnd) = spannable.getParagraphBounds(paragraphStart - 1)
val spans = spannable.getSpans(previousParagraphStart, previousParagraphEnd, type)

if (spans.isNotEmpty()) {
return spans.first()
}

return null
}

private fun <T>getNextParagraphSpan(spannable: Spannable, paragraphEnd: Int, type: Class<T>): T? {
if (paragraphEnd >= spannable.length - 1) return null

val (nextParagraphStart, nextParagraphEnd) = spannable.getParagraphBounds(paragraphEnd + 1)

val spans = spannable.getSpans(nextParagraphStart, nextParagraphEnd, type)

if (spans.isNotEmpty()) {
return spans.first()
}

return null
}

/**
* Applies a continuous span to the specified range.
* If the new range touches existing continuous spans, they are coalesced into a single span
*/
private fun <T>setContinuousSpan(spannable: Spannable, start: Int, end: Int, type: Class<T>) {
val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
val previousSpan = getPreviousParagraphSpan(spannable, start, type)
val nextSpan = getNextParagraphSpan(spannable, end, type)
var newStart = start
var newEnd = end

if (previousSpan != null) {
newStart = spannable.getSpanStart(previousSpan)
spannable.removeSpan(previousSpan)
}

if (nextSpan != null) {
newEnd = spannable.getSpanEnd(nextSpan)
spannable.removeSpan(nextSpan)
}

val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(newStart, newEnd)
spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}


private fun <T>setSpan(spannable: Spannable, type: Class<T>, start: Int, end: Int) {
if (type == EnrichedBlockQuoteSpan::class.java || type == EnrichedCodeBlockSpan::class.java) {
setContinuousSpan(spannable, start, end, type)
return
}

val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
Expand Down Expand Up @@ -94,14 +154,48 @@ class ParagraphStyles(private val view: EnrichedTextInputView) {
return spans.isNotEmpty()
}

private fun <T>mergeAdjacentStyleSpans(s: Editable, endCursorPosition: Int, type: Class<T>) {
val (start, end) = s.getParagraphBounds(endCursorPosition)
val currParagraphSpans = s.getSpans(start, end, type)

if (currParagraphSpans.isEmpty()) {
return
}

val currSpan = currParagraphSpans[0]
val nextSpan = getNextParagraphSpan(s, end, type)

if (nextSpan == null) {
return
}

val newStart = s.getSpanStart(currSpan)
val newEnd = s.getSpanEnd(nextSpan)

s.removeSpan(nextSpan)
s.removeSpan(currSpan)

val (safeStart, safeEnd) = s.getSafeSpanBoundaries(newStart, newEnd)
val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)

s.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}

fun afterTextChanged(s: Editable, endPosition: Int, previousTextLength: Int) {
var endCursorPosition = endPosition
val isBackspace = s.length < previousTextLength
val isNewLine = endCursorPosition == 0 || endCursorPosition > 0 && s[endCursorPosition - 1] == '\n'

for ((style, config) in EnrichedSpans.paragraphSpans) {
val spanState = view.spanState ?: continue
val styleStart = spanState.getStart(style) ?: continue
val styleStart = spanState.getStart(style)

if (styleStart == null) {
if (config.isContinuous) {
mergeAdjacentStyleSpans(s, endCursorPosition, config.clazz)
}
continue
}

if (isNewLine) {
if (!config.isContinuous) {
Expand Down
Loading