diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index 5552e7f3b..20d8a1f59 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -45,6 +45,8 @@ import org.wordpress.aztec.IHistoryListener import org.wordpress.aztec.ITextFormat import org.wordpress.aztec.glideloader.GlideImageLoader import org.wordpress.aztec.glideloader.GlideVideoThumbnailLoader +import org.wordpress.aztec.plugins.BackgroundColorButton +import org.wordpress.aztec.plugins.CssBackgroundColorPlugin import org.wordpress.aztec.plugins.CssUnderlinePlugin import org.wordpress.aztec.plugins.IMediaToolbarButton import org.wordpress.aztec.plugins.shortcodes.AudioShortcodePlugin @@ -89,6 +91,7 @@ open class MainActivity : AppCompatActivity(), private val BOLD = "Bold
" private val ITALIC = "Italic
" private val UNDERLINE = "Underline
" + private val BACKGROUND = "BACKGROUND
" private val STRIKETHROUGH = "Strikethrough
" // or or private val ORDERED = "
  1. Ordered
  2. should have color
" private val ORDERED_WITH_START = "

Start in 10 List:

" + @@ -185,6 +188,7 @@ open class MainActivity : AppCompatActivity(), BOLD + ITALIC + UNDERLINE + + BACKGROUND + STRIKETHROUGH + ORDERED + ORDERED_WITH_START + @@ -459,9 +463,13 @@ open class MainActivity : AppCompatActivity(), aztec.visualEditor.setCalypsoMode(false) aztec.sourceEditor?.setCalypsoMode(false) + aztec.visualEditor.setBackgroundSpanColor(resources.getColor(R.color.blue_dark)) + aztec.sourceEditor?.displayStyledAndFormattedHtml(EXAMPLE) aztec.addPlugin(CssUnderlinePlugin()) + aztec.addPlugin(CssBackgroundColorPlugin()) + aztec.addPlugin(BackgroundColorButton(visualEditor)) } if (savedInstanceState == null) { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt index b491cc1a6..77a7faf0b 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt @@ -28,7 +28,9 @@ import android.text.Spanned import androidx.appcompat.content.res.AppCompatResources import org.wordpress.aztec.plugins.IAztecPlugin import org.wordpress.aztec.plugins.html2visual.IHtmlTagHandler +import org.wordpress.aztec.source.CssStyleFormatter import org.wordpress.aztec.spans.AztecAudioSpan +import org.wordpress.aztec.spans.AztecBackgroundColorSpan import org.wordpress.aztec.spans.AztecHorizontalRuleSpan import org.wordpress.aztec.spans.AztecImageSpan import org.wordpress.aztec.spans.AztecMediaClickableSpan @@ -38,6 +40,7 @@ import org.wordpress.aztec.spans.AztecVideoSpan import org.wordpress.aztec.spans.HiddenHtmlSpan import org.wordpress.aztec.spans.IAztecAttributedSpan import org.wordpress.aztec.spans.IAztecNestable +import org.wordpress.aztec.spans.IAztecSpan import org.wordpress.aztec.spans.createAztecQuoteSpan import org.wordpress.aztec.spans.createHeadingSpan import org.wordpress.aztec.spans.createHiddenHtmlBlockSpan @@ -47,6 +50,7 @@ import org.wordpress.aztec.spans.createOrderedListSpan import org.wordpress.aztec.spans.createParagraphSpan import org.wordpress.aztec.spans.createPreformatSpan import org.wordpress.aztec.spans.createUnorderedListSpan +import org.wordpress.aztec.util.ColorConverter import org.wordpress.aztec.util.getLast import org.xml.sax.Attributes import java.util.ArrayList @@ -83,7 +87,7 @@ class AztecTagHandler(val context: Context, val plugins: List = Ar return true } SPAN -> { - val span = createHiddenHtmlSpan(tag, AztecAttributes(attributes), nestingLevel, alignmentRendering) + val span = handleBackgroundColorSpanTag(attributes, tag, nestingLevel) handleElement(output, opening, span) return true } @@ -154,6 +158,17 @@ class AztecTagHandler(val context: Context, val plugins: List = Ar return false } + private fun handleBackgroundColorSpanTag(attributes: Attributes, tag: String, nestingLevel: Int): IAztecSpan { + val attrs = AztecAttributes(attributes) + return if (CssStyleFormatter.containsStyleAttribute(attrs, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE) || (tagStack.isNotEmpty() && tagStack.last() is AztecBackgroundColorSpan)) { + val att = CssStyleFormatter.getStyleAttribute(attrs, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE) + val color = ColorConverter.getColorInt(att) + AztecBackgroundColorSpan(color) + } else { + createHiddenHtmlSpan(tag, attrs, nestingLevel, alignmentRendering) + } + } + private fun processTagHandlerPlugins(tag: String, opening: Boolean, output: Editable, attributes: Attributes, nestingLevel: Int): Boolean { plugins.filter { it is IHtmlTagHandler } .map { it as IHtmlTagHandler } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 2a5734cf7..d590c9995 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -366,6 +366,10 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown isInCalypsoMode = isCompatibleWithCalypso } + fun setBackgroundSpanColor(color: Int) { + inlineFormatter.backgroundSpanColor = color + } + fun setGutenbergMode(isCompatibleWithGutenberg: Boolean) { isInGutenbergMode = isCompatibleWithGutenberg } @@ -1143,6 +1147,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown AztecTextFormat.FORMAT_CITE, AztecTextFormat.FORMAT_UNDERLINE, AztecTextFormat.FORMAT_STRIKETHROUGH, + AztecTextFormat.FORMAT_BACKGROUND, AztecTextFormat.FORMAT_CODE -> inlineFormatter.toggle(textFormat) AztecTextFormat.FORMAT_BOLD, AztecTextFormat.FORMAT_STRONG -> inlineFormatter.toggleAny(ToolbarAction.BOLD.textFormats) @@ -1178,6 +1183,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown AztecTextFormat.FORMAT_CITE, AztecTextFormat.FORMAT_UNDERLINE, AztecTextFormat.FORMAT_STRIKETHROUGH, + AztecTextFormat.FORMAT_BACKGROUND, AztecTextFormat.FORMAT_CODE -> return inlineFormatter.containsInlineStyle(format, selStart, selEnd) AztecTextFormat.FORMAT_UNORDERED_LIST, AztecTextFormat.FORMAT_ORDERED_LIST -> return blockFormatter.containsList(format, selStart, selEnd) @@ -1191,7 +1197,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown } } - fun setToolbar(toolbar: IAztecToolbar) { + fun setToolbar(toolbar: IAztecToolbar?) { formatToolbar = toolbar } @@ -1633,6 +1639,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_STRIKETHROUGH, start, end) inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_UNDERLINE, start, end) inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_CODE, start, end) + inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_BACKGROUND, start, end) } fun removeBlockStylesFromRange(start: Int, end: Int, ignoreLineBounds: Boolean = false) { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTextFormat.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTextFormat.kt index 2f0bb4bae..a4b94dd5a 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTextFormat.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTextFormat.kt @@ -35,4 +35,5 @@ enum class AztecTextFormat : ITextFormat { FORMAT_FONT, FORMAT_MONOSPACE, FORMAT_CODE, + FORMAT_BACKGROUND, } 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 13b96f1e0..5aca2e2bf 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt @@ -11,14 +11,16 @@ import org.wordpress.aztec.AztecText import org.wordpress.aztec.AztecTextFormat import org.wordpress.aztec.Constants import org.wordpress.aztec.ITextFormat +import org.wordpress.aztec.R +import org.wordpress.aztec.spans.AztecBackgroundColorSpan import org.wordpress.aztec.spans.AztecCodeSpan import org.wordpress.aztec.spans.AztecStrikethroughSpan import org.wordpress.aztec.spans.AztecStyleBoldSpan import org.wordpress.aztec.spans.AztecStyleCiteSpan -import org.wordpress.aztec.spans.AztecStyleItalicSpan import org.wordpress.aztec.spans.AztecStyleEmphasisSpan -import org.wordpress.aztec.spans.AztecStyleStrongSpan +import org.wordpress.aztec.spans.AztecStyleItalicSpan import org.wordpress.aztec.spans.AztecStyleSpan +import org.wordpress.aztec.spans.AztecStyleStrongSpan import org.wordpress.aztec.spans.AztecUnderlineSpan import org.wordpress.aztec.spans.IAztecInlineSpan import org.wordpress.aztec.watchers.TextChangedEvent @@ -30,6 +32,8 @@ import java.util.ArrayList */ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormatter(editor) { + var backgroundSpanColor: Int? = null + data class CodeStyle(val codeBackground: Int, val codeBackgroundAlpha: Float, val codeColor: Int) fun toggle(textFormat: ITextFormat) { @@ -69,6 +73,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat AztecTextFormat.FORMAT_EMPHASIS, AztecTextFormat.FORMAT_CITE, AztecTextFormat.FORMAT_STRIKETHROUGH, + AztecTextFormat.FORMAT_BACKGROUND, AztecTextFormat.FORMAT_UNDERLINE, AztecTextFormat.FORMAT_CODE -> { applyInlineStyle(item, textChangedEvent.inputStart, textChangedEvent.inputEnd) @@ -111,6 +116,11 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat return } + if (textFormat == AztecTextFormat.FORMAT_BACKGROUND) { + //clear previous background before applying a new one to avoid problems when using multiple bg colors + removeBackgroundInSelection(selectionStart, selectionEnd) + } + var precedingSpan: IAztecInlineSpan? = null var followingSpan: IAztecInlineSpan? = null @@ -129,10 +139,10 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat if (spanEnd > start) { // ensure css style is applied - (precedingSpan as IAztecInlineSpan).applyInlineStyleAttributes(editableText, start, end) + spanToApply.applyInlineStyleAttributes(editableText, start, end) return@applyInlineStyle // we are adding text inside span - no need to do anything special } else { - applySpan(precedingSpan as IAztecInlineSpan, spanStart, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + applySpan(spanToApply, spanStart, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } } } @@ -148,8 +158,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat if (followingSpan != null) { val spanEnd = editableText.getSpanEnd(followingSpan) - applySpan(followingSpan as IAztecInlineSpan, start, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - editableText.setSpan(followingSpan, start, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + applySpan(spanToApply, start, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } } @@ -172,10 +181,27 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat applySpan(spanToApply, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } - joinStyleSpans(start, end) } + private fun removeBackgroundInSelection(selStart: Int, selEnd: Int) { + val spans = editableText.getSpans(selStart, selEnd, AztecBackgroundColorSpan::class.java) + spans.forEach { span -> + if (span != null) { + val currentSpanStart = editableText.getSpanStart(span) + val currentSpanEnd = editableText.getSpanEnd(span) + val color = span.backgroundColor + editableText.removeSpan(span) + if (selEnd < currentSpanEnd) { + editableText.setSpan(AztecBackgroundColorSpan(color), selEnd, currentSpanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + if (selStart > currentSpanStart) { + editableText.setSpan(AztecBackgroundColorSpan(color), currentSpanStart, selStart, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + } + } + private fun applySpan(span: IAztecInlineSpan, start: Int, end: Int, type: Int) { if (start > end || start < 0 || end > editableText.length) { // If an external logger is available log the error there. @@ -205,6 +231,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat AztecStrikethroughSpan::class.java -> return AztecTextFormat.FORMAT_STRIKETHROUGH AztecUnderlineSpan::class.java -> return AztecTextFormat.FORMAT_UNDERLINE AztecCodeSpan::class.java -> return AztecTextFormat.FORMAT_CODE + AztecBackgroundColorSpan::class.java -> return AztecTextFormat.FORMAT_BACKGROUND else -> return null } } @@ -221,7 +248,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat editableText.removeSpan(it) } } - // remove the CSS style span + removeInlineCssStyle() list.forEach { @@ -238,7 +265,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat joinStyleSpans(start, end) } - fun removeInlineCssStyle(start: Int = selectionStart, end: Int = selectionEnd) { + private fun removeInlineCssStyle(start: Int = selectionStart, end: Int = selectionEnd) { val spans = editableText.getSpans(start, end, ForegroundColorSpan::class.java) spans.forEach { editableText.removeSpan(it) @@ -254,12 +281,17 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat if (firstSpan is StyleSpan && secondSpan is StyleSpan) { return firstSpan.style == secondSpan.style } + // special check for BackgroundSpan + if (firstSpan is AztecBackgroundColorSpan && secondSpan is AztecBackgroundColorSpan) { + return firstSpan.backgroundColor == secondSpan.backgroundColor + } return firstSpan.javaClass == secondSpan.javaClass } // TODO: Check if there is more efficient way to tidy spans fun joinStyleSpans(start: Int, end: Int) { + // joins spans on the left if (start > 1) { val spansInSelection = editableText.getSpans(start, end, IAztecInlineSpan::class.java) @@ -349,6 +381,8 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle) : AztecFormat AztecTextFormat.FORMAT_STRIKETHROUGH -> return AztecStrikethroughSpan() AztecTextFormat.FORMAT_UNDERLINE -> return AztecUnderlineSpan() AztecTextFormat.FORMAT_CODE -> return AztecCodeSpan(codeStyle) + AztecTextFormat.FORMAT_BACKGROUND -> return AztecBackgroundColorSpan(backgroundSpanColor ?: R.color.background) + else -> return AztecStyleSpan(Typeface.NORMAL) } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/plugins/BackgroundColorButton.kt b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/BackgroundColorButton.kt new file mode 100644 index 000000000..3a03c17d9 --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/BackgroundColorButton.kt @@ -0,0 +1,29 @@ +package org.wordpress.aztec.plugins + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import org.wordpress.aztec.AztecText +import org.wordpress.aztec.R +import org.wordpress.aztec.toolbar.AztecToolbar +import org.wordpress.aztec.toolbar.IToolbarAction +import org.wordpress.aztec.toolbar.ToolbarAction + +class BackgroundColorButton(private val visualEditor: AztecText) : IToolbarButton { + + override val action: IToolbarAction = ToolbarAction.BACKGROUND + override val context = visualEditor.context!! + + override fun toggle() { + visualEditor.toggleFormatting(action.textFormats.first()) + } + + override fun inflateButton(parent: ViewGroup) { + LayoutInflater.from(context).inflate(R.layout.background_color_button, parent) + } + + override fun toolbarStateAboutToChange(toolbar: AztecToolbar, enable: Boolean) { + toolbar.findViewById(action.buttonId).isEnabled = enable + } + +} diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssBackgroundColorPlugin.kt b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssBackgroundColorPlugin.kt new file mode 100644 index 000000000..b002063d3 --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssBackgroundColorPlugin.kt @@ -0,0 +1,17 @@ +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.AztecBackgroundColorSpan + +class CssBackgroundColorPlugin : ISpanPreprocessor { + + override fun beforeSpansProcessed(spannable: SpannableStringBuilder) { + spannable.getSpans(0, spannable.length, AztecBackgroundColorSpan::class.java).forEach { + if (!CssStyleFormatter.containsStyleAttribute(it.attributes, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE)) { + CssStyleFormatter.addStyleAttribute(it.attributes, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE, it.getColorHex()) + } + } + } +} diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/source/CssStyleFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/source/CssStyleFormatter.kt index d977d920c..563d72579 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/source/CssStyleFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/source/CssStyleFormatter.kt @@ -1,10 +1,11 @@ package org.wordpress.aztec.source -import androidx.core.text.TextDirectionHeuristicsCompat import android.text.Editable import android.text.Layout import android.text.Spannable +import android.text.style.BackgroundColorSpan import android.text.style.ForegroundColorSpan +import androidx.core.text.TextDirectionHeuristicsCompat import org.wordpress.aztec.AztecAttributes import org.wordpress.aztec.spans.IAztecAlignmentSpan import org.wordpress.aztec.spans.IAztecAttributedSpan @@ -26,10 +27,11 @@ class CssStyleFormatter { val CSS_TEXT_DECORATION_ATTRIBUTE = "text-decoration" val CSS_TEXT_ALIGN_ATTRIBUTE = "text-align" val CSS_COLOR_ATTRIBUTE = "color" + val CSS_BACKGROUND_COLOR_ATTRIBUTE = "background-color" /** * Check the provided [attributedSpan] for the *style* attribute. If found, parse out the - * supported CSS style properties and use the results to create a [ForegroundColorSpan], + * supported CSS style properties and use the results to create a [ForegroundColorSpan] and/or [BackgroundColorSpan] * then add it to the provided [text]. * * Must be called immediately after the base [IAztecAttributedSpan] has been processed. @@ -41,6 +43,7 @@ class CssStyleFormatter { fun applyInlineStyleAttributes(text: Editable, attributedSpan: IAztecAttributedSpan, start: Int, end: Int) { if (attributedSpan.attributes.hasAttribute(STYLE_ATTRIBUTE) && start != end) { processColor(attributedSpan.attributes, text, start, end) + processBackgroundColor(attributedSpan.attributes, text, start, end) if (attributedSpan is IAztecParagraphStyle) { processAlignment(attributedSpan, text, start, end) } @@ -85,6 +88,16 @@ class CssStyleFormatter { } } + private fun processBackgroundColor(attributes: AztecAttributes, text: Editable, start: Int, end: Int) { + val colorAttrValue = getStyleAttribute(attributes, CSS_BACKGROUND_COLOR_ATTRIBUTE) + if (!colorAttrValue.isBlank()) { + val colorInt = ColorConverter.getColorInt(colorAttrValue) + if (colorInt != ColorConverter.COLOR_NOT_FOUND) { + text.setSpan(BackgroundColorSpan(colorInt), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + } + fun containsStyleAttribute(attributes: AztecAttributes, styleAttributeName: String): Boolean { return attributes.hasAttribute(STYLE_ATTRIBUTE) && getMatcher(attributes, styleAttributeName).find() } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecBackgroundColorSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecBackgroundColorSpan.kt new file mode 100644 index 000000000..650b0a33f --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecBackgroundColorSpan.kt @@ -0,0 +1,25 @@ +package org.wordpress.aztec.spans + +import android.graphics.Color +import android.text.TextPaint +import android.text.style.BackgroundColorSpan +import org.wordpress.aztec.AztecAttributes + +class AztecBackgroundColorSpan( + val color: Int +) : BackgroundColorSpan(color), IAztecInlineSpan { + + var alpha: Int = 220 + var tag: String = "span" + override var attributes: AztecAttributes = AztecAttributes() + + fun getColorHex(): String { + return java.lang.String.format("#%06X", 0xFFFFFF and color) + } + + override fun updateDrawState(textPaint: TextPaint) { + textPaint.bgColor = Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)) + } + + override val TAG = tag +} \ No newline at end of file diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index 4bcf86310..a5fb87946 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -378,7 +378,7 @@ class AztecToolbar : FrameLayout, IAztecToolbar, OnMenuItemClickListener { return editor != null && editor is AztecText } - override fun setEditor(editor: AztecText, sourceEditor: SourceViewEditText?) { + override fun setEditor(editor: AztecText?, sourceEditor: SourceViewEditText?) { this.sourceEditor = sourceEditor this.editor = editor @@ -450,7 +450,15 @@ class AztecToolbar : FrameLayout, IAztecToolbar, OnMenuItemClickListener { toolbarButtonPlugins.add(buttonPlugin) val button = findViewById(buttonPlugin.action.buttonId) - button.setOnClickListener { buttonPlugin.toggle() } + val isToolbarAction = ToolbarAction.values().contains(buttonPlugin.action) + button.setOnClickListener { + if (isToolbarAction) { + onToolbarAction(buttonPlugin.action) + } else { + buttonPlugin.toggle() + } + } + button.setBackgroundDrawableRes(buttonPlugin.action.buttonDrawableRes) setupMediaButtonForAccessibility(buttonPlugin) @@ -497,7 +505,7 @@ class AztecToolbar : FrameLayout, IAztecToolbar, OnMenuItemClickListener { if (action != ToolbarAction.ELLIPSIS_COLLAPSE && action != ToolbarAction.ELLIPSIS_EXPAND) { val view = findViewById(action.buttonId) - if (view.isChecked) actions.add(action) + if (view?.isChecked == true) actions.add(action) } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/IAztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/IAztecToolbar.kt index 589c2aa2a..cceb2320c 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/IAztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/IAztecToolbar.kt @@ -8,7 +8,7 @@ import org.wordpress.aztec.source.SourceViewEditText interface IAztecToolbar { fun onKeyUp(keyCode: Int, keyEvent: KeyEvent): Boolean fun addButton(buttonPlugin: IToolbarButton) - fun setEditor(editor: AztecText, sourceEditor: SourceViewEditText?) + fun setEditor(editor: AztecText?, sourceEditor: SourceViewEditText?) fun setToolbarListener(listener: IAztecToolbarClickListener) fun toggleMediaToolbar() fun toggleEditorMode() diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/ToolbarAction.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/ToolbarAction.kt index c15b0a685..ada0fd3e3 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/ToolbarAction.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/ToolbarAction.kt @@ -15,6 +15,12 @@ enum class ToolbarAction constructor( override val textFormats: Set = setOf()) : IToolbarAction { + BACKGROUND( + R.id.format_bar_button_background_color, + R.drawable.format_bar_button_background_selector, + ToolbarActionType.INLINE_STYLE, + setOf(AztecTextFormat.FORMAT_BACKGROUND) + ), ADD_MEDIA_COLLAPSE( R.id.format_bar_button_media_collapsed, R.drawable.format_bar_button_media_expanded_selector, diff --git a/aztec/src/main/res/drawable/format_bar_button_background.xml b/aztec/src/main/res/drawable/format_bar_button_background.xml new file mode 100644 index 000000000..d78ed9095 --- /dev/null +++ b/aztec/src/main/res/drawable/format_bar_button_background.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/aztec/src/main/res/drawable/format_bar_button_background_disabled.xml b/aztec/src/main/res/drawable/format_bar_button_background_disabled.xml new file mode 100644 index 000000000..f923b66c7 --- /dev/null +++ b/aztec/src/main/res/drawable/format_bar_button_background_disabled.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/aztec/src/main/res/drawable/format_bar_button_background_highlighted.xml b/aztec/src/main/res/drawable/format_bar_button_background_highlighted.xml new file mode 100644 index 000000000..b1779895c --- /dev/null +++ b/aztec/src/main/res/drawable/format_bar_button_background_highlighted.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/aztec/src/main/res/drawable/format_bar_button_background_selector.xml b/aztec/src/main/res/drawable/format_bar_button_background_selector.xml new file mode 100644 index 000000000..c8ab76fce --- /dev/null +++ b/aztec/src/main/res/drawable/format_bar_button_background_selector.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/aztec/src/main/res/layout/background_color_button.xml b/aztec/src/main/res/layout/background_color_button.xml new file mode 100644 index 000000000..908f3d1d3 --- /dev/null +++ b/aztec/src/main/res/layout/background_color_button.xml @@ -0,0 +1,10 @@ + + + + diff --git a/aztec/src/main/res/values/strings.xml b/aztec/src/main/res/values/strings.xml index a555c2f22..1104f8f97 100644 --- a/aztec/src/main/res/values/strings.xml +++ b/aztec/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ Heading Bold + Background Color Italic Underline Strikethrough diff --git a/aztec/src/test/kotlin/org/wordpress/aztec/AztecToolbarTest.kt b/aztec/src/test/kotlin/org/wordpress/aztec/AztecToolbarTest.kt index ce9934eb4..0fe823bdb 100644 --- a/aztec/src/test/kotlin/org/wordpress/aztec/AztecToolbarTest.kt +++ b/aztec/src/test/kotlin/org/wordpress/aztec/AztecToolbarTest.kt @@ -9,6 +9,7 @@ import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config +import org.wordpress.aztec.plugins.BackgroundColorButton import org.wordpress.aztec.source.SourceViewEditText import org.wordpress.aztec.toolbar.AztecToolbar @@ -26,6 +27,7 @@ class AztecToolbarTest { lateinit var italicButton: ToggleButton lateinit var strikeThroughButton: ToggleButton lateinit var underlineButton: ToggleButton + lateinit var backgroundColorButton: ToggleButton lateinit var quoteButton: ToggleButton lateinit var linkButton: ToggleButton lateinit var htmlButton: ToggleButton @@ -58,6 +60,8 @@ class AztecToolbarTest { alignCenterButton = toolbar.findViewById(R.id.format_bar_button_align_center) alignRightButton = toolbar.findViewById(R.id.format_bar_button_align_right) + toolbar.addButton(BackgroundColorButton(editText)) + backgroundColorButton = toolbar.findViewById(R.id.format_bar_button_background_color) } /** @@ -245,6 +249,47 @@ class AztecToolbarTest { Assert.assertEquals("underline", editText.toHtml()) } + /** + * Toggle backgroundColor button and type. + * + * @throws Exception + */ + @Test + @Throws(Exception::class) + fun testBackgroundColorTyping() { + Assert.assertFalse(backgroundColorButton.isChecked) + backgroundColorButton.performClick() + Assert.assertTrue(backgroundColorButton.isChecked) + + editText.append("backgroundColor") + Assert.assertEquals("backgroundColor", editText.toHtml()) + + backgroundColorButton.performClick() + Assert.assertFalse(backgroundColorButton.isChecked) + } + + /** + * Select text and toggle backgroundColor button. + * + * @throws Exception + */ + @Test + @Throws(Exception::class) + fun testBackgroundColorToggle() { + Assert.assertFalse(backgroundColorButton.isChecked) + + editText.append("backgroundColor") + editText.setSelection(0, editText.length()) + backgroundColorButton.performClick() + Assert.assertTrue(backgroundColorButton.isChecked) + Assert.assertEquals("backgroundColor", editText.toHtml()) + + backgroundColorButton.performClick() + Assert.assertFalse(backgroundColorButton.isChecked) + + Assert.assertEquals("backgroundColor", editText.toHtml()) + } + /** * Test inline style when applying and removing styles while typing. * @@ -549,7 +594,7 @@ class AztecToolbarTest { Assert.assertFalse(italicButton.isChecked) italicButton.performClick() - Assert.assertEquals("bolditalic", editText.toHtml()) + Assert.assertEquals("bolditalic", editText.toHtml()) } /**