Skip to content

Commit 007a890

Browse files
authored
Merge pull request #223 from wordpress-mobile/issue/162-adding-padding-to-block-elements
Issue/162 adding padding to block elements
2 parents da3dee4 + 19c2f16 commit 007a890

File tree

18 files changed

+198
-120
lines changed

18 files changed

+198
-120
lines changed

aztec/src/main/java/org/wordpress/aztec/Html.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.wordpress.aztec.spans.AztecCommentSpan;
4242
import org.wordpress.aztec.spans.AztecContentSpan;
4343
import org.wordpress.aztec.spans.AztecCursorSpan;
44-
import org.wordpress.aztec.spans.AztecListSpan;
44+
import org.wordpress.aztec.spans.AztecHeadingSpan;
4545
import org.wordpress.aztec.spans.AztecRelativeSizeSpan;
4646
import org.wordpress.aztec.spans.AztecStyleSpan;
4747
import org.wordpress.aztec.spans.AztecSubscriptSpan;
@@ -473,7 +473,7 @@ public Spanned convert() {
473473
// Fix flags and range for paragraph-type markup.
474474
Object[] obj = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ParagraphStyle.class);
475475
for (int i = 0; i < obj.length; i++) {
476-
if (obj[i] instanceof UnknownHtmlSpan || obj[i] instanceof AztecBlockSpan) {
476+
if (obj[i] instanceof UnknownHtmlSpan || obj[i] instanceof AztecBlockSpan || obj[i] instanceof AztecHeadingSpan) {
477477
continue;
478478
}
479479
int start = spannableStringBuilder.getSpanStart(obj[i]);

aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import android.text.Spanned
2525
import android.text.TextUtils
2626
import android.text.style.CharacterStyle
2727
import android.text.style.ImageSpan
28-
import android.text.style.ParagraphStyle
2928
import org.wordpress.aztec.spans.*
3029
import java.util.*
3130

@@ -231,9 +230,9 @@ class AztecParser {
231230
var i = 0
232231

233232
while (i < text.length) {
234-
next = text.nextSpanTransition(i, text.length, ParagraphStyle::class.java)
233+
next = text.nextSpanTransition(i, text.length, AztecParagraphStyle::class.java)
235234

236-
val styles = text.getSpans(i, next, ParagraphStyle::class.java)
235+
val styles = text.getSpans(i, next, AztecParagraphStyle::class.java)
237236

238237
if (styles.size == 2) {
239238
if (styles[0] is AztecListSpan && styles[1] is AztecQuoteSpan) {

aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class AztecText : EditText, TextWatcher {
7676

7777
lateinit var inlineFormatter: InlineFormatter
7878
lateinit var blockFormatter: BlockFormatter
79-
val lineBlockFormatter: LineBlockFormatter
79+
lateinit var lineBlockFormatter: LineBlockFormatter
8080
lateinit var linkFormatter: LinkFormatter
8181

8282
var imageGetter: Html.ImageGetter? = null
@@ -89,9 +89,6 @@ class AztecText : EditText, TextWatcher {
8989
fun onImeBack()
9090
}
9191

92-
init {
93-
lineBlockFormatter = LineBlockFormatter(this)
94-
}
9592

9693
constructor(context: Context) : super(context) {
9794
init(null)
@@ -129,26 +126,33 @@ class AztecText : EditText, TextWatcher {
129126
inlineFormatter = InlineFormatter(this,
130127
InlineFormatter.CodeStyle(
131128
array.getColor(R.styleable.AztecText_codeBackground, 0),
132-
array.getColor(R.styleable.AztecText_codeColor, 0)))
129+
array.getColor(R.styleable.AztecText_codeColor, 0)),
130+
LineBlockFormatter.HeaderStyle(
131+
array.getDimensionPixelSize(R.styleable.AztecText_blockVerticalPadding, 0)))
133132

134133
blockFormatter = BlockFormatter(this,
135134
BlockFormatter.ListStyle(
136135
array.getColor(R.styleable.AztecText_bulletColor, 0),
137136
array.getDimensionPixelSize(R.styleable.AztecText_bulletMargin, 0),
138137
array.getDimensionPixelSize(R.styleable.AztecText_bulletPadding, 0),
139-
array.getDimensionPixelSize(R.styleable.AztecText_bulletWidth, 0)),
138+
array.getDimensionPixelSize(R.styleable.AztecText_bulletWidth, 0),
139+
array.getDimensionPixelSize(R.styleable.AztecText_blockVerticalPadding, 0)),
140140
BlockFormatter.QuoteStyle(
141141
array.getColor(R.styleable.AztecText_quoteBackground, 0),
142142
array.getColor(R.styleable.AztecText_quoteColor, 0),
143143
array.getDimensionPixelSize(R.styleable.AztecText_quoteMargin, 0),
144144
array.getDimensionPixelSize(R.styleable.AztecText_quotePadding, 0),
145-
array.getDimensionPixelSize(R.styleable.AztecText_quoteWidth, 0)
145+
array.getDimensionPixelSize(R.styleable.AztecText_quoteWidth, 0),
146+
array.getDimensionPixelSize(R.styleable.AztecText_blockVerticalPadding, 0)
146147
))
147148

148149
linkFormatter = LinkFormatter(this, LinkFormatter.LinkStyle(array.getColor(
149150
R.styleable.AztecText_linkColor, 0),
150151
array.getBoolean(R.styleable.AztecText_linkUnderline, true)))
151152

153+
lineBlockFormatter = LineBlockFormatter(this, LineBlockFormatter.HeaderStyle(
154+
array.getDimensionPixelSize(R.styleable.AztecText_blockVerticalPadding, 0)))
155+
152156
array.recycle()
153157

154158
if (historyEnable && historySize <= 0) {
@@ -210,7 +214,9 @@ class AztecText : EditText, TextWatcher {
210214
val retainedSelectionStart = customState.getInt("selection_start")
211215
val retainedSelectionEnd = customState.getInt("selection_end")
212216

213-
setSelection(retainedSelectionStart, retainedSelectionEnd)
217+
if (retainedSelectionEnd < editableText.length) {
218+
setSelection(retainedSelectionStart, retainedSelectionEnd)
219+
}
214220

215221

216222
val isDialogVisible = customState.getBoolean("isUrlDialogVisible", false)
@@ -665,6 +671,15 @@ class AztecText : EditText, TextWatcher {
665671
editable.removeSpan(it)
666672
editable.setSpan(inlineFormatter.makeInlineSpan(it.javaClass, it.attributes), spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
667673
}
674+
675+
val headingSpan = editable.getSpans(start, end, AztecHeadingSpan::class.java)
676+
headingSpan.forEach {
677+
val spanStart = editable.getSpanStart(it)
678+
val spanEnd = editable.getSpanEnd(it)
679+
editable.removeSpan(it)
680+
editable.setSpan(AztecHeadingSpan(it.textFormat, it.attributes,
681+
lineBlockFormatter.headerStyle.verticalPadding), spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
682+
}
668683
}
669684

670685
fun disableTextChangedListener() {

aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import java.util.*
1313

1414
class BlockFormatter(editor: AztecText, listStyle: ListStyle, quoteStyle: QuoteStyle) : AztecFormatter(editor) {
1515

16-
data class ListStyle(val indicatorColor: Int, val indicatorMargin: Int, val indicatorPadding: Int, val indicatorWidth: Int)
17-
data class QuoteStyle(val quoteBackground: Int, val quoteColor: Int, val quoteMargin: Int, val quotePadding: Int, val quoteWidth: Int)
16+
data class ListStyle(val indicatorColor: Int, val indicatorMargin: Int, val indicatorPadding: Int, val indicatorWidth: Int, val verticalPadding: Int)
17+
data class QuoteStyle(val quoteBackground: Int, val quoteColor: Int, val quoteMargin: Int, val quotePadding: Int, val quoteWidth: Int, val verticalPadding: Int)
1818

1919
val listStyle: ListStyle
2020
val quoteStyle: QuoteStyle

aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,13 @@ import org.wordpress.aztec.spans.*
1111
import java.util.*
1212

1313

14-
class InlineFormatter(editor: AztecText, codeStyle: CodeStyle) : AztecFormatter(editor) {
14+
class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, val headerStyle: LineBlockFormatter.HeaderStyle) : AztecFormatter(editor) {
1515

1616
data class CarryOverSpan(val span: AztecInlineSpan, val start: Int, val end: Int)
1717
data class CodeStyle(val codeBackground: Int, val codeColor: Int)
1818

1919
val carryOverSpans = ArrayList<CarryOverSpan>()
20-
val codeStyle: CodeStyle
2120

22-
init {
23-
this.codeStyle = codeStyle
24-
}
2521

2622
fun toggleBold() {
2723
if (!containsInlineStyle(TextFormat.FORMAT_BOLD)) {
@@ -55,7 +51,7 @@ class InlineFormatter(editor: AztecText, codeStyle: CodeStyle) : AztecFormatter(
5551
}
5652
}
5753

58-
fun toggleCode(){
54+
fun toggleCode() {
5955
if (!containsInlineStyle(TextFormat.FORMAT_CODE)) {
6056
applyInlineStyle(TextFormat.FORMAT_CODE)
6157
} else {
@@ -356,7 +352,7 @@ class InlineFormatter(editor: AztecText, codeStyle: CodeStyle) : AztecFormatter(
356352
TextFormat.FORMAT_HEADING_3,
357353
TextFormat.FORMAT_HEADING_4,
358354
TextFormat.FORMAT_HEADING_5,
359-
TextFormat.FORMAT_HEADING_6 -> return AztecHeadingSpan(textFormat)
355+
TextFormat.FORMAT_HEADING_6 -> return AztecHeadingSpan(textFormat, "", headerStyle.verticalPadding)
360356
TextFormat.FORMAT_BOLD -> return AztecStyleSpan(Typeface.BOLD)
361357
TextFormat.FORMAT_ITALIC -> return AztecStyleSpan(Typeface.ITALIC)
362358
TextFormat.FORMAT_STRIKETHROUGH -> return AztecStrikethroughSpan()

aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import org.wordpress.aztec.spans.*
1212
import java.util.*
1313

1414

15-
class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) {
15+
class LineBlockFormatter(editor: AztecText, val headerStyle: LineBlockFormatter.HeaderStyle) : AztecFormatter(editor) {
16+
17+
data class HeaderStyle(val verticalPadding: Int)
1618

1719
fun applyHeading(textFormat: TextFormat) {
1820
headingClear()
@@ -48,7 +50,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) {
4850
editableText.setSpan(spanAtNewLIne, spanStart + 1, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
4951
} else if (!isHeadingSplitRequired && editableText.length > textChangedEvent.inputStart + 1 && editableText[textChangedEvent.inputStart + 1] == '\n') {
5052
editableText.setSpan(spanAtNewLIne, spanStart, spanEnd - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
51-
} else if(!isHeadingSplitRequired && textChangedEvent.inputStart + 1 == spanEnd && editableText[textChangedEvent.inputStart] == '\n'){
53+
} else if (!isHeadingSplitRequired && textChangedEvent.inputStart + 1 == spanEnd && editableText[textChangedEvent.inputStart] == '\n') {
5254
editableText.setSpan(spanAtNewLIne, spanStart, spanEnd - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
5355
}
5456
}
@@ -135,7 +137,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) {
135137
}
136138

137139
if (headingStart < headingEnd) {
138-
editableText.setSpan(AztecHeadingSpan(textFormat), headingStart, headingEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
140+
editableText.setSpan(AztecHeadingSpan(textFormat, "", headerStyle.verticalPadding), headingStart, headingEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
139141
}
140142
}
141143

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
package org.wordpress.aztec.spans
22

33

4-
interface AztecBlockSpan : AztecContentSpan, AztecLineBlockSpan
4+
interface AztecBlockSpan : AztecContentSpan, AztecLineBlockSpan, AztecParagraphStyle

aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecHeadingSpan.kt

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
package org.wordpress.aztec.spans
22

3+
import android.graphics.Paint
4+
import android.text.Spanned
35
import android.text.TextPaint
46
import android.text.TextUtils
7+
import android.text.style.LineHeightSpan
58
import android.text.style.MetricAffectingSpan
9+
import android.text.style.UpdateLayout
610
import org.wordpress.aztec.TextFormat
711

8-
class AztecHeadingSpan @JvmOverloads constructor(var textFormat: TextFormat, attrs: String = "") : MetricAffectingSpan(), AztecLineBlockSpan, AztecContentSpan, AztecInlineSpan {
12+
class AztecHeadingSpan @JvmOverloads constructor(var textFormat: TextFormat, attrs: String = "", val verticalPadding: Int) : MetricAffectingSpan(),
13+
AztecLineBlockSpan, AztecContentSpan, AztecInlineSpan, LineHeightSpan, UpdateLayout {
914

1015
lateinit var heading: Heading
1116
override var attributes: String = attrs
1217

18+
var previousFontMetrics: Paint.FontMetricsInt? = null
19+
var previousTextScale: Float = 1.0f
20+
1321
companion object {
1422
private val SCALE_H1: Float = 1.73f
1523
private val SCALE_H2: Float = 1.32f
@@ -31,7 +39,46 @@ class AztecHeadingSpan @JvmOverloads constructor(var textFormat: TextFormat, att
3139
}
3240
}
3341

34-
constructor(tag: String, attrs: String = "") : this(getTextFormat(tag), attrs) {
42+
constructor(tag: String, attrs: String = "", verticalPadding: Int = 0) : this(getTextFormat(tag), attrs, verticalPadding)
43+
44+
override fun chooseHeight(text: CharSequence, start: Int, end: Int, spanstartv: Int, v: Int, fm: Paint.FontMetricsInt) {
45+
val spanned = text as Spanned
46+
val spanStart = spanned.getSpanStart(this)
47+
val spanEnd = spanned.getSpanEnd(this)
48+
49+
//save original font metrics
50+
if (previousFontMetrics == null) {
51+
previousFontMetrics = Paint.FontMetricsInt()
52+
previousFontMetrics!!.top = fm.top
53+
previousFontMetrics!!.ascent = fm.ascent
54+
previousFontMetrics!!.bottom = fm.bottom
55+
previousFontMetrics!!.descent = fm.descent
56+
}
57+
58+
var addedTopPadding = false
59+
var addedBottomPadding = false
60+
61+
if (start == spanStart || start < spanStart) {
62+
fm.ascent -= verticalPadding
63+
fm.top -= verticalPadding
64+
addedTopPadding = true
65+
}
66+
if (end == spanEnd || spanEnd < end) {
67+
fm.descent += verticalPadding
68+
fm.bottom += verticalPadding
69+
addedBottomPadding = true
70+
}
71+
72+
//apply original font metrics to lines that should not have vertical padding
73+
if (!addedTopPadding) {
74+
fm.ascent = previousFontMetrics!!.ascent
75+
fm.top = previousFontMetrics!!.top
76+
}
77+
78+
if (!addedBottomPadding) {
79+
fm.descent = previousFontMetrics!!.descent
80+
fm.bottom = previousFontMetrics!!.bottom
81+
}
3582

3683
}
3784

@@ -61,6 +108,12 @@ class AztecHeadingSpan @JvmOverloads constructor(var textFormat: TextFormat, att
61108
}
62109

63110
override fun updateMeasureState(textPaint: TextPaint) {
111+
//when font size changes - reset cached font metrics to reapply vertical padding
112+
if (previousTextScale != heading.scale) {
113+
previousFontMetrics = null
114+
}
115+
previousTextScale = heading.scale
116+
64117
textPaint.textSize *= heading.scale
65118
}
66119

@@ -96,6 +149,6 @@ class AztecHeadingSpan @JvmOverloads constructor(var textFormat: TextFormat, att
96149
}
97150

98151
override fun clone(): Any {
99-
return AztecHeadingSpan(textFormat, attributes)
152+
return AztecHeadingSpan(textFormat, attributes, verticalPadding)
100153
}
101154
}
Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,62 @@
11
package org.wordpress.aztec.spans
22

3+
import android.graphics.Paint
4+
import android.text.Spanned
5+
import android.text.style.LeadingMarginSpan
6+
import android.text.style.LineHeightSpan
7+
import android.text.style.UpdateLayout
8+
9+
10+
abstract class AztecListSpan(val verticalPadding: Int) : LeadingMarginSpan.Standard(0), AztecBlockSpan, LineHeightSpan, UpdateLayout {
11+
12+
abstract var lastItem: AztecListItemSpan
13+
14+
override fun chooseHeight(text: CharSequence, start: Int, end: Int, spanstartv: Int, v: Int, fm: Paint.FontMetricsInt) {
15+
val spanned = text as Spanned
16+
val spanStart = spanned.getSpanStart(this)
17+
val spanEnd = spanned.getSpanEnd(this)
18+
19+
if (start === spanStart || start < spanStart) {
20+
fm.ascent -= verticalPadding
21+
fm.top -= verticalPadding
22+
}
23+
if (end === spanEnd || spanEnd < end) {
24+
fm.descent += verticalPadding
25+
fm.bottom += verticalPadding
26+
}
27+
}
28+
29+
fun getIndexOfProcessedLine(text: CharSequence, end: Int): Int {
30+
val spanStart = (text as Spanned).getSpanStart(this)
31+
val spanEnd = text.getSpanEnd(this)
32+
33+
val listText = text.subSequence(spanStart, spanEnd)
34+
35+
val textBeforeBeforeEnd = listText.substring(0, end - spanStart)
36+
val lineIndex = textBeforeBeforeEnd.length - textBeforeBeforeEnd.replace("\n", "").length
37+
return lineIndex + 1
38+
}
39+
40+
fun getTotalNumberOfLines(text: CharSequence): Int {
41+
val spanStart = (text as Spanned).getSpanStart(this)
42+
val spanEnd = text.getSpanEnd(this)
43+
44+
return text.substring(spanStart..spanEnd - 2).split("\n").count()
45+
}
46+
47+
fun getIndicatorAdjustment(text: CharSequence, end: Int): Int {
48+
var adjustment = 0
49+
50+
val totalNumberOfLinesInList = getTotalNumberOfLines(text)
51+
val currentLineNumber = getIndexOfProcessedLine(text, end)
52+
53+
if (totalNumberOfLinesInList > 1 && currentLineNumber == 1) {
54+
adjustment = if (this is AztecOrderedListSpan) 0 else verticalPadding
55+
} else if ((totalNumberOfLinesInList > 1 && totalNumberOfLinesInList == currentLineNumber) || totalNumberOfLinesInList == 1) {
56+
adjustment = -verticalPadding
57+
}
58+
59+
return adjustment
60+
}
361

4-
interface AztecListSpan : AztecBlockSpan {
5-
var lastItem: AztecListItemSpan
662
}

aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ import android.graphics.Paint
55
import android.graphics.Rect
66
import android.graphics.drawable.Drawable
77
import android.text.style.ImageSpan
8-
import android.text.style.ParagraphStyle
98
import android.view.View
109
import android.widget.Toast
11-
1210
import org.wordpress.android.util.DisplayUtils
1311

14-
class AztecMediaSpan(val context: Context, drawable: Drawable, source: String) : ImageSpan(drawable), ParagraphStyle {
12+
class AztecMediaSpan(val context: Context, drawable: Drawable, source: String) : ImageSpan(drawable), AztecParagraphStyle{
1513
private val html = source
1614

1715
companion object {

0 commit comments

Comments
 (0)