Skip to content
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
@@ -0,0 +1,39 @@
package com.swmansion.enriched.common

import android.text.Spannable
import com.swmansion.enriched.common.spans.EnrichedAlignmentSpan
import com.swmansion.enriched.common.spans.interfaces.EnrichedInlineSpan

// Higher priority spans are processed first, so styles with lower priorities are painted on top of previously applied styles.
// For example, inline styles are applied on top of paragraph styles, allowing them to override paragraph-level styling.
// Alignment styles are applied last, ensuring they position the final, fully styled text.
object EnrichedSpanFlags {
private const val ALIGNMENT_SPAN_PRIORITY = 0
private const val INLINE_SPAN_PRIORITY = 1
private const val PARAGRAPH_SPAN_PRIORITY = 2

@JvmStatic
@JvmOverloads
fun forSpan(
span: Any?,
baseFlags: Int = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
): Int {
val priority =
when (span) {
is EnrichedAlignmentSpan -> ALIGNMENT_SPAN_PRIORITY
is EnrichedInlineSpan -> INLINE_SPAN_PRIORITY
else -> PARAGRAPH_SPAN_PRIORITY
}
return applyPriority(baseFlags, priority)
}

private fun applyPriority(
flags: Int,
priority: Int,
): Int {
// Cleaning up priority bits
val cleared = flags and Spannable.SPAN_PRIORITY.inv()
// Injecting priority bits
return cleared or ((priority shl Spannable.SPAN_PRIORITY_SHIFT) and Spannable.SPAN_PRIORITY)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.text.TextUtils;
import android.text.style.ParagraphStyle;
import com.swmansion.enriched.common.EnrichedConstants;
import com.swmansion.enriched.common.EnrichedSpanFlags;
import com.swmansion.enriched.common.spans.EnrichedAlignmentSpan;
import com.swmansion.enriched.common.spans.EnrichedBoldSpan;
import com.swmansion.enriched.common.spans.EnrichedCheckboxListSpan;
Expand Down Expand Up @@ -467,10 +468,7 @@ public Spanned convert() {
if (end == start) {
mSpannableStringBuilder.removeSpan(obj[i]);
} else {
// TODO: verify if Spannable.SPAN_EXCLUSIVE_EXCLUSIVE does not break anything.
// Previously it was SPAN_PARAGRAPH. I've changed that in order to fix ranges for list
// items.
mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mSpannableStringBuilder.setSpan(obj[i], start, end, EnrichedSpanFlags.forSpan(obj[i]));
}
}

Expand Down Expand Up @@ -505,7 +503,7 @@ public Spanned convert() {

mSpannableStringBuilder.removeSpan(zeroWidthSpaceSpan);
mSpannableStringBuilder.setSpan(
zeroWidthSpaceSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
zeroWidthSpaceSpan, start, end, EnrichedSpanFlags.forSpan(zeroWidthSpaceSpan));
}

return mSpannableStringBuilder;
Expand Down Expand Up @@ -802,7 +800,7 @@ private static void setSpanFromMark(Spannable text, Object mark, Object... spans
int len = text.length();
if (where != len) {
for (Object span : spans) {
text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(span, where, len, EnrichedSpanFlags.forSpan(span));
}
}
}
Expand All @@ -825,7 +823,7 @@ private static void setParagraphSpanFromMark(Editable text, Object mark, Object.

if (where != len) {
for (Object span : spans) {
text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(span, where, len, EnrichedSpanFlags.forSpan(span));
}
}
}
Expand All @@ -850,11 +848,9 @@ private static <T> void startImg(

int len = text.length();
text.append("");
text.setSpan(
spanFactory.createImageSpan(src, Integer.parseInt(width), Integer.parseInt(height)),
len,
text.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Object imageSpan =
spanFactory.createImageSpan(src, Integer.parseInt(width), Integer.parseInt(height));
text.setSpan(imageSpan, len, text.length(), EnrichedSpanFlags.forSpan(imageSpan));
}

private static void startA(Editable text, Attributes attributes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontStyle
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontWeight
import com.swmansion.enriched.common.EnrichedConstants
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.common.GumboNormalizer
import com.swmansion.enriched.common.parser.EnrichedParser
import com.swmansion.enriched.common.pixelFromSpOrDp
Expand Down Expand Up @@ -277,7 +278,7 @@ class EnrichedTextView : AppCompatTextView {

spannable.removeSpan(span)
val newSpan = span.rebuildWithStyle(enrichedStyle)
spannable.setSpan(newSpan, start, end, flags)
spannable.setSpan(newSpan, start, end, EnrichedSpanFlags.forSpan(newSpan, flags))
modified = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontStyle
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontWeight
import com.swmansion.enriched.common.EnrichedConstants
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.common.GumboNormalizer
import com.swmansion.enriched.common.parser.EnrichedParser
import com.swmansion.enriched.common.pixelFromSpOrDp
Expand Down Expand Up @@ -1108,7 +1109,7 @@ class EnrichedTextInputView :

spannable.removeSpan(span)
val newSpan = span.rebuildWithStyle(htmlStyle)
spannable.setSpan(newSpan, start, end, flags)
spannable.setSpan(newSpan, start, end, EnrichedSpanFlags.forSpan(newSpan, flags))
}

if (shouldEmitStateChange) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.text.Editable
import android.text.Spannable
import android.text.SpannableStringBuilder
import com.swmansion.enriched.common.EnrichedConstants
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.textinput.EnrichedTextInputView
import com.swmansion.enriched.textinput.spans.EnrichedInputAlignmentSpan
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
Expand All @@ -24,12 +25,8 @@ class AlignmentStyles(
flags: Int = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
) {
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
spannable.setSpan(
EnrichedInputAlignmentSpan(cssValue),
safeStart,
safeEnd,
flags,
)
val span = EnrichedInputAlignmentSpan(cssValue)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span, flags))
}

private fun toCssValue(alignment: String): String =
Expand Down Expand Up @@ -302,7 +299,9 @@ class AlignmentStyles(
// INCLUSIVE_EXCLUSIVE is intentional here: autoStretchAlignmentSpan will convert
// it to EXCLUSIVE_EXCLUSIVE once the merge is complete.
val (safeStart, safeEnd) = s.getSafeSpanBoundaries(paraStart, paraEnd)
dominantTopSpan?.let { s.setSpan(it, safeStart, safeEnd, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) }
dominantTopSpan?.let {
s.setSpan(it, safeStart, safeEnd, EnrichedSpanFlags.forSpan(it, Spannable.SPAN_INCLUSIVE_EXCLUSIVE))
}
return cursorPosition
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.swmansion.enriched.textinput.styles

import android.text.Editable
import android.text.Spannable
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.textinput.EnrichedTextInputView
import com.swmansion.enriched.textinput.spans.EnrichedSpans
import com.swmansion.enriched.textinput.utils.getSafeSpanBoundaries
Expand Down Expand Up @@ -41,7 +42,7 @@ class InlineStyles(

val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(minimum, maximum)
spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
}

private fun <T> setAndMergeSpans(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import com.swmansion.enriched.common.EnrichedConstants
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.textinput.EnrichedTextInputView
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
import com.swmansion.enriched.textinput.spans.EnrichedInputOrderedListSpan
Expand Down Expand Up @@ -66,18 +67,18 @@ class ListStyles(
when (name) {
EnrichedSpans.UNORDERED_LIST -> {
val span = EnrichedInputUnorderedListSpan(view.htmlStyle)
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
}

EnrichedSpans.ORDERED_LIST -> {
val index = getOrderedListIndex(spannable, safeStart)
val span = EnrichedInputOrderedListSpan(index, view.htmlStyle)
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
}

EnrichedSpans.CHECKBOX_LIST -> {
val span = EnrichedInputCheckboxListSpan(isChecked ?: false, view.htmlStyle)
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))

// Invalidate layout to update checkbox drawing in case checkbox is bigger than line height
view.layoutManager.invalidateLayout()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.text.Editable
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.util.Log
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.textinput.EnrichedTextInputView
import com.swmansion.enriched.textinput.spans.EnrichedSpans
import com.swmansion.enriched.textinput.spans.interfaces.EnrichedInputSpan
Expand Down Expand Up @@ -89,7 +90,7 @@ class ParagraphStyles(
}

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

private fun <T> setSpan(
Expand All @@ -105,7 +106,7 @@ class ParagraphStyles(

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)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
}

// Removes spans of the given type in the specified range.
Expand Down Expand Up @@ -232,7 +233,7 @@ class ParagraphStyles(
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)
s.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
}

private fun handleConflictsDuringNewlineDeletion(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import com.swmansion.enriched.common.EnrichedConstants
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.textinput.EnrichedTextInputView
import com.swmansion.enriched.textinput.spans.EnrichedInputImageSpan
import com.swmansion.enriched.textinput.spans.EnrichedInputLinkSpan
Expand Down Expand Up @@ -63,7 +64,7 @@ class ParametrizedStyles(
val spanEnd = start + text.length
val span = EnrichedInputLinkSpan(url, view.htmlStyle, true)
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, spanEnd)
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))

Comment thread
kacperzolkiewski marked this conversation as resolved.
view.selection?.validateStyles()
isSettingLinkSpan = false
Expand Down Expand Up @@ -160,7 +161,7 @@ class ParametrizedStyles(
span,
safeStart,
safeEnd,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
EnrichedSpanFlags.forSpan(span),
)
}
}
Expand Down Expand Up @@ -373,7 +374,7 @@ class ParametrizedStyles(
val span = EnrichedInputMentionSpan(text, indicator, attributes, view.htmlStyle)
val spanEnd = start + text.length
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, spanEnd)
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))

val hasSpaceAtTheEnd = spannable.length > safeEnd && spannable[safeEnd] == ' '
if (!hasSpaceAtTheEnd) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.swmansion.enriched.textinput.utils
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.common.spans.interfaces.EnrichedBlockSpan
import com.swmansion.enriched.common.spans.interfaces.EnrichedParagraphSpan
import com.swmansion.enriched.common.spans.interfaces.EnrichedSpan
Expand Down Expand Up @@ -144,7 +145,7 @@ fun Spannable.mergeSpannables(
val (_, newParagraphEnd) = builder.getParagraphBounds(spanStart, pasteEnd)
val flags = builder.getSpanFlags(span)
builder.removeSpan(span)
builder.setSpan(span, spanStart, newParagraphEnd, flags)
builder.setSpan(span, spanStart, newParagraphEnd, EnrichedSpanFlags.forSpan(span, flags))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.text.Spanned
import android.util.Log
import android.view.MotionEvent
import android.widget.TextView
import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
import org.json.JSONObject

Expand Down Expand Up @@ -78,7 +79,7 @@ fun TextView.setCheckboxClickListener() {

// Reapply span so changes are visible without need to redraw entire TextView
spannable.removeSpan(span)
spannable.setSpan(span, start, end, flags)
spannable.setSpan(span, start, end, EnrichedSpanFlags.forSpan(span, flags))

// For focused input, ensure cursor is active for affected paragraph
if (tv.isFocused) {
Expand Down
Loading