From 445fef5bd651d552c61bbda3546ab0ce7b879e0b Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 17 Mar 2017 11:00:05 +0200 Subject: [PATCH 1/3] Add a test for #289 --- .../kotlin/org/wordpress/aztec/HeadingTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/aztec/src/test/kotlin/org/wordpress/aztec/HeadingTest.kt b/aztec/src/test/kotlin/org/wordpress/aztec/HeadingTest.kt index 074493658..eafbbabd0 100644 --- a/aztec/src/test/kotlin/org/wordpress/aztec/HeadingTest.kt +++ b/aztec/src/test/kotlin/org/wordpress/aztec/HeadingTest.kt @@ -234,4 +234,20 @@ class HeadingTest() { editText.text.delete(mark - 1, mark) Assert.assertEquals("

Heading 1unstyled

", editText.toHtml()) } + + @Test + @Throws(Exception::class) + fun addHeading_issue289() { + editText.fromHtml("

Heading 1

") + + safeAppend(editText, "\n") + + editText.setSelection(safeLength(editText)) + editText.toggleFormatting(TextFormat.FORMAT_HEADING_2) + Assert.assertEquals("

Heading 1

", editText.toHtml()) + + safeAppend(editText, "Heading 2") + Assert.assertEquals("

Heading 1

Heading 2

", editText.toHtml()) + } + } \ No newline at end of file From 761c70555889d8bc07e95a62349111b44fa6021b Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 17 Mar 2017 11:02:18 +0200 Subject: [PATCH 2/3] Heading style is needed for equality check --- .../aztec/formatting/BlockFormatter.kt | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt index 700def0d6..4a97f4ef2 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt @@ -317,8 +317,8 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle if (numberOfLines == numberOfLinesWithSpanApplied) { removeBlockStyle(blockElementType) } else { - applyBlock(blockElementType, startOfBlock + 1, - (if (endOfBlock == editableText.length) endOfBlock else endOfBlock + 1), nestingLevel) + applyBlock(makeBlockSpan(blockElementType, nestingLevel), startOfBlock + 1, + (if (endOfBlock == editableText.length) endOfBlock else endOfBlock + 1)) } } else { @@ -327,31 +327,39 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle val startOfLine = boundsOfSelectedText.start val endOfLine = boundsOfSelectedText.endInclusive - val spanToApply = getOuterBlockSpanType(blockElementType) + val nestingLevel = AztecNestable.getNestingLevelAt(editableText, start) + 1 + + val spanToApply = makeBlockSpan(blockElementType, nestingLevel) var startOfBlock: Int = startOfLine var endOfBlock: Int = endOfLine if (startOfLine != 0) { - val spansOnPreviousLine = editableText.getSpans(startOfLine - 1, startOfLine - 1, spanToApply).firstOrNull() - if (spansOnPreviousLine != null) { + val spansOnPreviousLine = editableText.getSpans(startOfLine - 1, startOfLine - 1, spanToApply.javaClass) + .firstOrNull() + // if same span type is found (also check for heading style equality if a heading) extend the start + if (spansOnPreviousLine != null + && (spansOnPreviousLine !is AztecHeadingSpan + || spansOnPreviousLine.heading == (spanToApply as AztecHeadingSpan).heading)) { startOfBlock = editableText.getSpanStart(spansOnPreviousLine) liftBlock(blockElementType, startOfBlock, endOfBlock) } } if (endOfLine != editableText.length) { - val spanOnNextLine = editableText.getSpans(endOfLine + 1, endOfLine + 1, spanToApply).firstOrNull() - if (spanOnNextLine != null) { + val spanOnNextLine = editableText.getSpans(endOfLine + 1, endOfLine + 1, spanToApply.javaClass) + .firstOrNull() + // if same span type is found (also check for heading style equality if a heading) extend the end + if (spanOnNextLine != null + && (spanOnNextLine !is AztecHeadingSpan + || spanOnNextLine.heading == (spanToApply as AztecHeadingSpan).heading)) { endOfBlock = editableText.getSpanEnd(spanOnNextLine) liftBlock(blockElementType, startOfBlock, endOfBlock) } } - val nestingLevel = AztecNestable.getNestingLevelAt(editableText, start) + 1 - - applyBlock(blockElementType, startOfBlock, endOfBlock, nestingLevel) + applyBlock(spanToApply, startOfBlock, endOfBlock) //if the line was empty trigger onSelectionChanged manually to update toolbar buttons status // if (isEmptyLine) { @@ -360,22 +368,17 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle } } - private fun applyBlock(textFormat: TextFormat, start: Int, end: Int, nestingLevel: Int, attrs: String = "") { - when (textFormat) { - TextFormat.FORMAT_ORDERED_LIST -> applyListBlock(AztecOrderedListSpan(nestingLevel, attrs, listStyle), start, end, nestingLevel) - TextFormat.FORMAT_UNORDERED_LIST -> applyListBlock(AztecUnorderedListSpan(nestingLevel, attrs, listStyle), start, end, nestingLevel) - TextFormat.FORMAT_QUOTE -> BlockHandler.set(editableText, AztecQuoteSpan(nestingLevel, attrs, quoteStyle), start, end) - TextFormat.FORMAT_HEADING_1, - TextFormat.FORMAT_HEADING_2, - TextFormat.FORMAT_HEADING_3, - TextFormat.FORMAT_HEADING_4, - TextFormat.FORMAT_HEADING_5, - TextFormat.FORMAT_HEADING_6 -> applyHeadingBlock(AztecHeadingSpan(nestingLevel, textFormat, attrs, headerStyle), start, end, nestingLevel) - else -> editableText.setSpan(ParagraphSpan(nestingLevel, attrs), start, end, Spanned.SPAN_PARAGRAPH) + private fun applyBlock(blockSpan: AztecBlockSpan, start: Int, end: Int) { + when (blockSpan) { + is AztecOrderedListSpan -> applyListBlock(blockSpan, start, end) + is AztecUnorderedListSpan -> applyListBlock(blockSpan, start, end) + is AztecQuoteSpan -> BlockHandler.set(editableText, blockSpan, start, end) + is AztecHeadingSpan -> applyHeadingBlock(blockSpan, start, end) + else -> editableText.setSpan(blockSpan, start, end, Spanned.SPAN_PARAGRAPH) } } - private fun applyListBlock(listSpan: AztecListSpan, start: Int, end: Int, nestingLevel: Int) { + private fun applyListBlock(listSpan: AztecListSpan, start: Int, end: Int) { BlockHandler.set(editableText, listSpan, start, end) val lines = TextUtils.split(editableText.substring(start, end), "\n") @@ -389,11 +392,11 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle if (lineLength == 0) continue - ListItemHandler.newListItem(editableText, start + lineStart, start + lineEnd, nestingLevel + 1) + ListItemHandler.newListItem(editableText, start + lineStart, start + lineEnd, listSpan.nestingLevel + 1) } } - private fun applyHeadingBlock(headingSpan: AztecHeadingSpan, start: Int, end: Int, nestingLevel: Int) { + private fun applyHeadingBlock(headingSpan: AztecHeadingSpan, start: Int, end: Int) { val lines = TextUtils.split(editableText.substring(start, end), "\n") for (i in lines.indices) { val lineLength = lines[i].length From 6c7e8acd1c4074d2a9ebff25704db1e16fe0b763 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 17 Mar 2017 11:02:43 +0200 Subject: [PATCH 3/3] Better heading boundary calculation --- .../org/wordpress/aztec/formatting/BlockFormatter.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt index 4a97f4ef2..0a775c95c 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt @@ -399,16 +399,15 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle private fun applyHeadingBlock(headingSpan: AztecHeadingSpan, start: Int, end: Int) { val lines = TextUtils.split(editableText.substring(start, end), "\n") for (i in lines.indices) { - val lineLength = lines[i].length + val splitLength = lines[i].length - val lineStart = (0..i - 1).sumBy { lines[it].length + 1 } - val lineEnd = (lineStart + lineLength).let { - if ((start + it) != editableText.length) it + 1 else it // include the newline or not - } + val lineStart = start + (0..i - 1).sumBy { lines[it].length + 1 } + val lineEnd = Math.min(lineStart + splitLength + 1, end) // +1 to include the newline + val lineLength = lineEnd - lineStart if (lineLength == 0) continue - HeadingHandler.cloneHeading(editableText, headingSpan, start + lineStart, start + lineEnd) + HeadingHandler.cloneHeading(editableText, headingSpan, lineStart, lineEnd) } }