Skip to content

Commit

Permalink
Merge pull request #899 from wordpress-mobile/gutenberg_alignment
Browse files Browse the repository at this point in the history
Update Heading, Preformat, and HiddenHtml Spans to allow view-level alignment
  • Loading branch information
mchowning authored Apr 24, 2020
2 parents 6c08f3f + 49237c7 commit 1e09b7d
Show file tree
Hide file tree
Showing 30 changed files with 575 additions and 190 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [1.3.42](https://github.com/wordpress-mobile/AztecEditor-Android/releases/tag/v1.3.42)
### Changed
- Update block-based span classes to allow view level alignment rendering (#899)

## [1.3.41](https://github.com/wordpress-mobile/AztecEditor-Android/releases/tag/v1.3.41)
### Changed
- Add option to disable collapsing of whitespaces
Expand Down
12 changes: 12 additions & 0 deletions aztec/src/main/kotlin/org/wordpress/aztec/AlignmentRendering.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.wordpress.aztec

/**
* With [SPAN_LEVEL] any alignment must be specified at the span level. Importantly, this
* means that the View's gravity will always be ignored in determining the rendering of
* the text's alignment.
*
* With [VIEW_LEVEL] alignment, the rendering of alignment is determined by the View's gravity.
* Note that it is not possible to update the underlying alignment using [AztecText.toggleFormatting]
* when you are using [VIEW_LEVEL] alignment rendering.
*/
enum class AlignmentRendering { SPAN_LEVEL, VIEW_LEVEL }
7 changes: 4 additions & 3 deletions aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ import java.util.ArrayList
import java.util.Collections
import java.util.Comparator

class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = listOf(),
class AztecParser @JvmOverloads constructor(private val alignmentRendering: AlignmentRendering,
val plugins: List<IAztecPlugin> = listOf(),
private val ignoredTags: List<String> = listOf("body", "html")) {
/**
* A faster version of fromHtml(), intended for inspecting the span structure only. It doesn't prepare the text for
Expand All @@ -68,7 +69,7 @@ class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = li
val tidySource = tidy(source)

val spanned = SpannableString(Html.fromHtml(tidySource,
AztecTagHandler(context, plugins), context, plugins, ignoredTags, true))
AztecTagHandler(context, plugins, alignmentRendering), context, plugins, ignoredTags, true))

postprocessSpans(spanned)

Expand All @@ -82,7 +83,7 @@ class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = li
val tidySource = if (shouldSkipTidying) source else tidy(source)

val spanned = SpannableStringBuilder(Html.fromHtml(tidySource,
AztecTagHandler(context, plugins), context, plugins, ignoredTags, shouldIgnoreWhitespace))
AztecTagHandler(context, plugins, alignmentRendering), context, plugins, ignoredTags, shouldIgnoreWhitespace))

addVisualNewlinesToBlockElements(spanned)
markBlockElementsAsParagraphs(spanned)
Expand Down
42 changes: 25 additions & 17 deletions aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,30 @@ import androidx.appcompat.content.res.AppCompatResources
import org.wordpress.aztec.plugins.IAztecPlugin
import org.wordpress.aztec.plugins.html2visual.IHtmlTagHandler
import org.wordpress.aztec.spans.AztecAudioSpan
import org.wordpress.aztec.spans.AztecHeadingSpan
import org.wordpress.aztec.spans.AztecHorizontalRuleSpan
import org.wordpress.aztec.spans.AztecImageSpan
import org.wordpress.aztec.spans.AztecListItemSpan
import org.wordpress.aztec.spans.AztecMediaClickableSpan
import org.wordpress.aztec.spans.AztecMediaSpan
import org.wordpress.aztec.spans.AztecOrderedListSpan
import org.wordpress.aztec.spans.AztecPreformatSpan
import org.wordpress.aztec.spans.AztecQuoteSpan
import org.wordpress.aztec.spans.AztecStrikethroughSpan
import org.wordpress.aztec.spans.AztecUnorderedListSpan
import org.wordpress.aztec.spans.AztecVideoSpan
import org.wordpress.aztec.spans.HiddenHtmlBlock
import org.wordpress.aztec.spans.HiddenHtmlSpan
import org.wordpress.aztec.spans.IAztecAttributedSpan
import org.wordpress.aztec.spans.IAztecNestable
import org.wordpress.aztec.spans.createAztecQuoteSpan
import org.wordpress.aztec.spans.createHeadingSpan
import org.wordpress.aztec.spans.createHiddenHtmlBlockSpan
import org.wordpress.aztec.spans.createHiddenHtmlSpan
import org.wordpress.aztec.spans.createListItemSpan
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.getLast
import org.xml.sax.Attributes
import java.util.ArrayList

class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = ArrayList()) : Html.TagHandler {
class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = ArrayList(), private val alignmentRendering: AlignmentRendering
) : Html.TagHandler {
private val loadingDrawable: Drawable

// Simple LIFO stack to track the html tag nesting for easy reference when we need to handle the ending of a tag
Expand All @@ -72,31 +74,35 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar

when (tag.toLowerCase()) {
LIST_LI -> {
handleElement(output, opening, AztecListItemSpan(nestingLevel, AztecAttributes(attributes)))
val span = createListItemSpan(nestingLevel, alignmentRendering, AztecAttributes(attributes))
handleElement(output, opening, span)
return true
}
STRIKETHROUGH_S, STRIKETHROUGH_STRIKE, STRIKETHROUGH_DEL -> {
handleElement(output, opening, AztecStrikethroughSpan(tag, AztecAttributes(attributes)))
return true
}
SPAN -> {
handleElement(output, opening, HiddenHtmlSpan(tag, AztecAttributes(attributes), nestingLevel))
val span = createHiddenHtmlSpan(tag, AztecAttributes(attributes), nestingLevel, alignmentRendering)
handleElement(output, opening, span)
return true
}
DIV, FIGURE, FIGCAPTION, SECTION -> {
handleElement(output, opening, HiddenHtmlBlock(tag, AztecAttributes(attributes), nestingLevel))
val hiddenHtmlBlockSpan = createHiddenHtmlBlockSpan(tag, alignmentRendering, nestingLevel, AztecAttributes(attributes))
handleElement(output, opening, hiddenHtmlBlockSpan)
return true
}
LIST_UL -> {
handleElement(output, opening, AztecUnorderedListSpan(nestingLevel, AztecAttributes(attributes)))
handleElement(output, opening, createUnorderedListSpan(nestingLevel, alignmentRendering, AztecAttributes(attributes)))
return true
}
LIST_OL -> {
handleElement(output, opening, AztecOrderedListSpan(nestingLevel, AztecAttributes(attributes)))
handleElement(output, opening, createOrderedListSpan(nestingLevel, alignmentRendering, AztecAttributes(attributes)))
return true
}
BLOCKQUOTE -> {
handleElement(output, opening, AztecQuoteSpan(nestingLevel, AztecAttributes(attributes)))
val span = createAztecQuoteSpan(nestingLevel, AztecAttributes(attributes), alignmentRendering)
handleElement(output, opening, span)
return true
}
IMAGE -> {
Expand All @@ -118,7 +124,8 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
return true
}
PARAGRAPH -> {
handleElement(output, opening, createParagraphSpan(nestingLevel, AztecAttributes(attributes)))
val paragraphSpan = createParagraphSpan(nestingLevel, alignmentRendering, AztecAttributes(attributes))
handleElement(output, opening, paragraphSpan)
return true
}
LINE -> {
Expand All @@ -133,12 +140,13 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
return true
}
PREFORMAT -> {
handleElement(output, opening, AztecPreformatSpan(nestingLevel, AztecAttributes(attributes)))
val preformatSpan = createPreformatSpan(nestingLevel, alignmentRendering, AztecAttributes(attributes))
handleElement(output, opening, preformatSpan)
return true
}
else -> {
if (tag.length == 2 && Character.toLowerCase(tag[0]) == 'h' && tag[1] >= '1' && tag[1] <= '6') {
handleElement(output, opening, AztecHeadingSpan(nestingLevel, tag, AztecAttributes(attributes)))
handleElement(output, opening, createHeadingSpan(nestingLevel, tag, AztecAttributes(attributes), alignmentRendering))
return true
}
}
Expand Down
30 changes: 21 additions & 9 deletions aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown

val DEFAULT_IMAGE_WIDTH = 800

val DEFAULT_ALIGNMENT_RENDERING = AlignmentRendering.SPAN_LEVEL

var watchersNestingLevel: Int = 0

private fun getPlaceholderDrawableFromResID(context: Context, @DrawableRes drawableId: Int, maxImageWidthForVisualEditor: Int): BitmapDrawable {
Expand Down Expand Up @@ -248,7 +250,8 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
var commentsVisible = resources.getBoolean(R.bool.comments_visible)

var isInCalypsoMode = true
var isInGutenbergMode = false
var isInGutenbergMode: Boolean = false
val alignmentRendering: AlignmentRendering

var consumeHistoryEvent: Boolean = false

Expand Down Expand Up @@ -334,14 +337,22 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
}

constructor(context: Context) : super(context) {
alignmentRendering = DEFAULT_ALIGNMENT_RENDERING
init(null)
}

constructor(context: Context, alignmentRendering: AlignmentRendering) : super(context) {
this.alignmentRendering = alignmentRendering
init(null)
}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
alignmentRendering = DEFAULT_ALIGNMENT_RENDERING
init(attrs)
}

constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
alignmentRendering = DEFAULT_ALIGNMENT_RENDERING
init(attrs)
}

Expand Down Expand Up @@ -416,7 +427,8 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
styles.getColor(R.styleable.AztecText_preformatBackground, 0),
getPreformatBackgroundAlpha(styles),
styles.getColor(R.styleable.AztecText_preformatColor, 0),
verticalParagraphMargin)
verticalParagraphMargin),
alignmentRendering
)

linkFormatter = LinkFormatter(this, LinkFormatter.LinkStyle(styles.getColor(
Expand Down Expand Up @@ -591,9 +603,9 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
// will have the chance to run their "beforeTextChanged" and "onTextChanged" with the same string!

BlockElementWatcher(this)
.add(HeadingHandler())
.add(HeadingHandler(alignmentRendering))
.add(ListHandler())
.add(ListItemHandler())
.add(ListItemHandler(alignmentRendering))
.add(QuoteHandler())
.add(PreformatHandler())
.install(this)
Expand Down Expand Up @@ -1168,7 +1180,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown

open fun fromHtml(source: String, isInit: Boolean = true) {
val builder = SpannableStringBuilder()
val parser = AztecParser(plugins)
val parser = AztecParser(alignmentRendering, plugins)

var cleanSource = CleaningUtils.cleanNestedBoldTags(source)
cleanSource = Format.removeSourceEditorFormatting(cleanSource, isInCalypsoMode, isInGutenbergMode)
Expand Down Expand Up @@ -1313,7 +1325,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
}

private fun parseHtml(content: Spannable, withCursorTag: Boolean): String {
val parser = AztecParser(plugins)
val parser = AztecParser(alignmentRendering, plugins)
val output: SpannableStringBuilder
try {
output = SpannableStringBuilder(content)
Expand Down Expand Up @@ -1559,7 +1571,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
// Convert selected text to html and add it to clipboard
fun copy(editable: Editable, start: Int, end: Int) {
val selectedText = editable.subSequence(start, end)
val parser = AztecParser(plugins)
val parser = AztecParser(alignmentRendering, plugins)
val output = SpannableStringBuilder(selectedText)

clearMetaSpans(output)
Expand Down Expand Up @@ -1625,7 +1637,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown

if (clip.itemCount > 0) {
val textToPaste = if (asPlainText) clip.getItemAt(0).coerceToText(context).toString()
else clip.getItemAt(0).coerceToHtmlText(AztecParser(plugins))
else clip.getItemAt(0).coerceToHtmlText(AztecParser(alignmentRendering, plugins))

val oldHtml = toPlainHtml().replace("<aztec_cursor>", "")
val newHtml = oldHtml.replace(Constants.REPLACEMENT_MARKER_STRING, textToPaste + "<" + AztecCursorSpan.AZTEC_CURSOR_TAG + ">")
Expand Down Expand Up @@ -1743,7 +1755,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
val spanStart = text.getSpanStart(unknownHtmlSpan)

val textBuilder = SpannableStringBuilder()
textBuilder.append(AztecParser(plugins).fromHtml(source.getPureHtml(), context).trim())
textBuilder.append(AztecParser(alignmentRendering, plugins).fromHtml(source.getPureHtml(), context).trim())
setSelection(spanStart)

disableTextChangedListener()
Expand Down
Loading

0 comments on commit 1e09b7d

Please sign in to comment.