diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index a5d97bfec..3ac7ac57e 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -1344,6 +1344,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown AztecTextFormat.FORMAT_CODE -> inlineFormatter.toggle(textFormat) AztecTextFormat.FORMAT_BOLD, AztecTextFormat.FORMAT_STRONG -> inlineFormatter.toggleAny(ToolbarAction.BOLD.textFormats) + AztecTextFormat.FORMAT_MARK -> inlineFormatter.toggle(textFormat) AztecTextFormat.FORMAT_UNORDERED_LIST -> blockFormatter.toggleUnorderedList() AztecTextFormat.FORMAT_TASK_LIST -> blockFormatter.toggleTaskList() AztecTextFormat.FORMAT_ORDERED_LIST -> blockFormatter.toggleOrderedList() diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt index 287ed33e5..f72e99a13 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt @@ -37,6 +37,7 @@ import org.wordpress.aztec.watchers.TextChangedEvent class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val highlightStyle: HighlightStyle) : AztecFormatter(editor) { var backgroundSpanColor: Int? = null + var markStyleColor: String? = null data class CodeStyle(val codeBackground: Int, val codeBackgroundAlpha: Float, val codeColor: Int) data class HighlightStyle(@ColorRes val color: Int) @@ -107,12 +108,8 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h applyInlineStyle(item, textChangedEvent.inputStart, textChangedEvent.inputEnd) } AztecTextFormat.FORMAT_MARK -> { - // For cases of an empty mark tag, either at the beginning of the text or in between - if (textChangedEvent.inputStart == 0 && textChangedEvent.inputEnd == 1) { - applyMarkInlineStyle(textChangedEvent.inputStart, textChangedEvent.inputEnd) - } else { - applyInlineStyle(item, textChangedEvent.inputStart, textChangedEvent.inputEnd) - } + applyInlineStyle(item, textChangedEvent.inputStart, textChangedEvent.inputEnd) + applyAfterMarkInlineStyle(textChangedEvent.inputStart, textChangedEvent.inputEnd) } else -> { // do nothing @@ -128,6 +125,16 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h val newStart = if (start > end) end else start // if there is END_OF_BUFFER_MARKER at the end of or range, extend the range to include it + // Clear Mark formatting styles + if (!editor.selectedStyles.contains(AztecTextFormat.FORMAT_MARK) && start >= 1 && end > 1 ) { + val previousMarkSpan = editableText.getSpans(start - 1, start, MarkSpan::class.java) + val markSpan = editableText.getSpans(start, end, MarkSpan::class.java) + if (markSpan.isNotEmpty() || previousMarkSpan.isNotEmpty()) { + removeInlineCssStyle(start, end) + return + } + } + // remove lingering empty spans when removing characters if (start > end) { editableText.getSpans(newStart, end, IAztecInlineSpan::class.java) @@ -250,10 +257,40 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h } } - private fun applyMarkInlineStyle(start: Int = selectionStart, end: Int = selectionEnd) { - val previousSpans = editableText.getSpans(start, end, MarkSpan::class.java) - previousSpans.forEach { - it.applyInlineStyleAttributes(editableText, start, end) + private fun applyAfterMarkInlineStyle(start: Int = selectionStart, end: Int = selectionEnd) { + // If there's no new mark style color to update, it skips applying the style updates. + if (markStyleColor == null) { + return + } + + val spans = editableText.getSpans(start, end, MarkSpan::class.java) + spans.forEach { span -> + if (span != null) { + val color = span.getTextColor() + val currentSpanStart = editableText.getSpanStart(span) + val currentSpanEnd = editableText.getSpanEnd(span) + + if (end < currentSpanEnd) { + markStyleColor = null + return + } + + if (!color.equals(markStyleColor, ignoreCase = true)) { + editableText.removeSpan(span) + editableText.setSpan( + MarkSpan(AztecAttributes(), color), + currentSpanStart, + start, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + editableText.setSpan( + MarkSpan(AztecAttributes(), markStyleColor), + start, + end, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } } } @@ -444,7 +481,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h AztecTextFormat.FORMAT_HIGHLIGHT -> { HighlightSpan.create(context = editor.context, defaultStyle = highlightStyle) } - AztecTextFormat.FORMAT_MARK -> MarkSpan() + AztecTextFormat.FORMAT_MARK -> MarkSpan(AztecAttributes(), markStyleColor) else -> AztecStyleSpan(Typeface.NORMAL) } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/plugins/MarkPlugin.kt b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/MarkPlugin.kt new file mode 100644 index 000000000..e3320e1ef --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/MarkPlugin.kt @@ -0,0 +1,25 @@ +package org.wordpress.aztec.plugins + +import android.text.SpannableStringBuilder +import org.wordpress.aztec.plugins.visual2html.ISpanPreprocessor +import org.wordpress.aztec.source.CssStyleFormatter +import org.wordpress.aztec.spans.MarkSpan + +class MarkPlugin : ISpanPreprocessor { + + override fun beforeSpansProcessed(spannable: SpannableStringBuilder) { + spannable.getSpans(0, spannable.length, MarkSpan::class.java).forEach { + if (!CssStyleFormatter.containsStyleAttribute(it.attributes, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE)) { + CssStyleFormatter.addStyleAttribute(it.attributes, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE, "rgba(0, 0, 0, 0)") + } + + if (!CssStyleFormatter.containsStyleAttribute(it.attributes, CssStyleFormatter.CSS_COLOR_ATTRIBUTE)) { + CssStyleFormatter.addStyleAttribute(it.attributes, CssStyleFormatter.CSS_COLOR_ATTRIBUTE, it.getTextColor()) + } + + if (!it.attributes.hasAttribute("class")) { + it.attributes.setValue("class", "has-inline-color") + } + } + } +} diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/MarkSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/MarkSpan.kt index 8ab898b3b..5c9b0f9a1 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/MarkSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/MarkSpan.kt @@ -1,12 +1,45 @@ package org.wordpress.aztec.spans +import android.graphics.Color import android.text.TextPaint import android.text.style.CharacterStyle import org.wordpress.aztec.AztecAttributes +import org.wordpress.aztec.source.CssStyleFormatter -class MarkSpan(override var attributes: AztecAttributes = AztecAttributes()) : CharacterStyle(), IAztecInlineSpan { +class MarkSpan : CharacterStyle, IAztecInlineSpan { override var TAG = "mark" - override fun updateDrawState(tp: TextPaint?) { + override var attributes: AztecAttributes = AztecAttributes() + private val textColorValue: Int? + + constructor(attributes: AztecAttributes = AztecAttributes()) : super() { + this.attributes = attributes + + val color = CssStyleFormatter.getStyleAttribute(attributes, + CssStyleFormatter.CSS_COLOR_ATTRIBUTE) + textColorValue = if (color.isNotEmpty()) { + Color.parseColor(color) + } else { + null + } + } + + constructor(attributes: AztecAttributes = AztecAttributes(), colorString: String?) : super() { + this.attributes = attributes + + textColorValue = if (colorString != null) { + Color.parseColor(colorString) + } else { + null + } + } + + override fun updateDrawState(tp: TextPaint) { + textColorValue?.let { tp.color = it } + } + + fun getTextColor(): String { + val currentColor = textColorValue ?: 0 + return String.format("#%06X", 0xFFFFFF and currentColor) } }