Skip to content

Commit 2c2401f

Browse files
committed
feat: add heading styles
1 parent 684eeec commit 2c2401f

25 files changed

+515
-44
lines changed

android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ class EnrichedTextInputView : AppCompatEditText {
381381
EnrichedSpans.H1 -> paragraphStyles?.toggleStyle(EnrichedSpans.H1)
382382
EnrichedSpans.H2 -> paragraphStyles?.toggleStyle(EnrichedSpans.H2)
383383
EnrichedSpans.H3 -> paragraphStyles?.toggleStyle(EnrichedSpans.H3)
384+
EnrichedSpans.H4 -> paragraphStyles?.toggleStyle(EnrichedSpans.H4)
385+
EnrichedSpans.H5 -> paragraphStyles?.toggleStyle(EnrichedSpans.H5)
386+
EnrichedSpans.H6 -> paragraphStyles?.toggleStyle(EnrichedSpans.H6)
384387
EnrichedSpans.CODE_BLOCK -> paragraphStyles?.toggleStyle(EnrichedSpans.CODE_BLOCK)
385388
EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.toggleStyle(EnrichedSpans.BLOCK_QUOTE)
386389
EnrichedSpans.ORDERED_LIST -> listStyles?.toggleStyle(EnrichedSpans.ORDERED_LIST)
@@ -401,6 +404,9 @@ class EnrichedTextInputView : AppCompatEditText {
401404
EnrichedSpans.H1 -> paragraphStyles?.removeStyle(EnrichedSpans.H1, start, end)
402405
EnrichedSpans.H2 -> paragraphStyles?.removeStyle(EnrichedSpans.H2, start, end)
403406
EnrichedSpans.H3 -> paragraphStyles?.removeStyle(EnrichedSpans.H3, start, end)
407+
EnrichedSpans.H4 -> paragraphStyles?.removeStyle(EnrichedSpans.H4, start, end)
408+
EnrichedSpans.H5 -> paragraphStyles?.removeStyle(EnrichedSpans.H5, start, end)
409+
EnrichedSpans.H6 -> paragraphStyles?.removeStyle(EnrichedSpans.H6, start, end)
404410
EnrichedSpans.CODE_BLOCK -> paragraphStyles?.removeStyle(EnrichedSpans.CODE_BLOCK, start, end)
405411
EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.removeStyle(EnrichedSpans.BLOCK_QUOTE, start, end)
406412
EnrichedSpans.ORDERED_LIST -> listStyles?.removeStyle(EnrichedSpans.ORDERED_LIST, start, end)
@@ -424,6 +430,9 @@ class EnrichedTextInputView : AppCompatEditText {
424430
EnrichedSpans.H1 -> paragraphStyles?.getStyleRange()
425431
EnrichedSpans.H2 -> paragraphStyles?.getStyleRange()
426432
EnrichedSpans.H3 -> paragraphStyles?.getStyleRange()
433+
EnrichedSpans.H4 -> paragraphStyles?.getStyleRange()
434+
EnrichedSpans.H5 -> paragraphStyles?.getStyleRange()
435+
EnrichedSpans.H6 -> paragraphStyles?.getStyleRange()
427436
EnrichedSpans.CODE_BLOCK -> paragraphStyles?.getStyleRange()
428437
EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.getStyleRange()
429438
EnrichedSpans.ORDERED_LIST -> listStyles?.getStyleRange()

android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,18 @@ class EnrichedTextInputViewManager : SimpleViewManager<EnrichedTextInputView>(),
231231
view?.verifyAndToggleStyle(EnrichedSpans.H3)
232232
}
233233

234+
override fun toggleH4(view: EnrichedTextInputView?) {
235+
view?.verifyAndToggleStyle(EnrichedSpans.H4)
236+
}
237+
238+
override fun toggleH5(view: EnrichedTextInputView?) {
239+
view?.verifyAndToggleStyle(EnrichedSpans.H5)
240+
}
241+
242+
override fun toggleH6(view: EnrichedTextInputView?) {
243+
view?.verifyAndToggleStyle(EnrichedSpans.H6)
244+
}
245+
234246
override fun toggleCodeBlock(view: EnrichedTextInputView?) {
235247
view?.verifyAndToggleStyle(EnrichedSpans.CODE_BLOCK)
236248
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.swmansion.enriched.spans
2+
3+
import android.graphics.Typeface
4+
import android.text.TextPaint
5+
import android.text.style.AbsoluteSizeSpan
6+
import com.swmansion.enriched.spans.interfaces.EnrichedHeadingSpan
7+
import com.swmansion.enriched.styles.HtmlStyle
8+
9+
class EnrichedH4Span(private val htmlStyle: HtmlStyle) : AbsoluteSizeSpan(htmlStyle.h4FontSize), EnrichedHeadingSpan {
10+
override fun updateDrawState(tp: TextPaint) {
11+
super.updateDrawState(tp)
12+
val bold = htmlStyle.h4Bold
13+
if (bold) {
14+
tp.typeface = Typeface.create(tp.typeface, Typeface.BOLD)
15+
}
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.swmansion.enriched.spans
2+
3+
import android.graphics.Typeface
4+
import android.text.TextPaint
5+
import android.text.style.AbsoluteSizeSpan
6+
import com.swmansion.enriched.spans.interfaces.EnrichedHeadingSpan
7+
import com.swmansion.enriched.styles.HtmlStyle
8+
9+
class EnrichedH5Span(private val htmlStyle: HtmlStyle) : AbsoluteSizeSpan(htmlStyle.h5FontSize), EnrichedHeadingSpan {
10+
override fun updateDrawState(tp: TextPaint) {
11+
super.updateDrawState(tp)
12+
val bold = htmlStyle.h5Bold
13+
if (bold) {
14+
tp.typeface = Typeface.create(tp.typeface, Typeface.BOLD)
15+
}
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.swmansion.enriched.spans
2+
3+
import android.graphics.Typeface
4+
import android.text.TextPaint
5+
import android.text.style.AbsoluteSizeSpan
6+
import com.swmansion.enriched.spans.interfaces.EnrichedHeadingSpan
7+
import com.swmansion.enriched.styles.HtmlStyle
8+
9+
class EnrichedH6Span(private val htmlStyle: HtmlStyle) : AbsoluteSizeSpan(htmlStyle.h6FontSize), EnrichedHeadingSpan {
10+
override fun updateDrawState(tp: TextPaint) {
11+
super.updateDrawState(tp)
12+
val bold = htmlStyle.h6Bold
13+
if (bold) {
14+
tp.typeface = Typeface.create(tp.typeface, Typeface.BOLD)
15+
}
16+
}
17+
}

android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ object EnrichedSpans {
2323
const val H1 = "h1"
2424
const val H2 = "h2"
2525
const val H3 = "h3"
26+
const val H4 = "h4"
27+
const val H5 = "h5"
28+
const val H6 = "h6"
2629
const val BLOCK_QUOTE = "block_quote"
2730
const val CODE_BLOCK = "code_block"
2831

@@ -47,6 +50,9 @@ object EnrichedSpans {
4750
H1 to ParagraphSpanConfig(EnrichedH1Span::class.java, false),
4851
H2 to ParagraphSpanConfig(EnrichedH2Span::class.java, false),
4952
H3 to ParagraphSpanConfig(EnrichedH3Span::class.java, false),
53+
H4 to ParagraphSpanConfig(EnrichedH4Span::class.java, false),
54+
H5 to ParagraphSpanConfig(EnrichedH5Span::class.java, false),
55+
H6 to ParagraphSpanConfig(EnrichedH6Span::class.java, false),
5056
BLOCK_QUOTE to ParagraphSpanConfig(EnrichedBlockQuoteSpan::class.java, true),
5157
CODE_BLOCK to ParagraphSpanConfig(EnrichedCodeBlockSpan::class.java, true),
5258
)
@@ -80,25 +86,34 @@ object EnrichedSpans {
8086
blockingStyles = arrayOf(CODE_BLOCK)
8187
),
8288
H1 to StylesMergingConfig(
83-
conflictingStyles = arrayOf(H2, H3, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
89+
conflictingStyles = arrayOf(H2, H3, H4, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
8490
),
8591
H2 to StylesMergingConfig(
86-
conflictingStyles = arrayOf(H1, H3, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
92+
conflictingStyles = arrayOf(H1, H3, H4, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
8793
),
8894
H3 to StylesMergingConfig(
89-
conflictingStyles = arrayOf(H1, H2, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
95+
conflictingStyles = arrayOf(H1, H2, H4, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
96+
),
97+
H4 to StylesMergingConfig(
98+
conflictingStyles = arrayOf(H1, H2, H3, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
99+
),
100+
H5 to StylesMergingConfig(
101+
conflictingStyles = arrayOf(H1, H2, H3, H4, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
102+
),
103+
H6 to StylesMergingConfig(
104+
conflictingStyles = arrayOf(H1, H2, H3, H4, H5, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK),
90105
),
91106
BLOCK_QUOTE to StylesMergingConfig(
92-
conflictingStyles = arrayOf(H1, H2, H3, CODE_BLOCK, ORDERED_LIST, UNORDERED_LIST),
107+
conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, CODE_BLOCK, ORDERED_LIST, UNORDERED_LIST),
93108
),
94109
CODE_BLOCK to StylesMergingConfig(
95-
conflictingStyles = arrayOf(H1, H2, H3, BOLD, ITALIC, UNDERLINE, STRIKETHROUGH, UNORDERED_LIST, ORDERED_LIST, BLOCK_QUOTE, INLINE_CODE),
110+
conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, BOLD, ITALIC, UNDERLINE, STRIKETHROUGH, UNORDERED_LIST, ORDERED_LIST, BLOCK_QUOTE, INLINE_CODE),
96111
),
97112
UNORDERED_LIST to StylesMergingConfig(
98-
conflictingStyles = arrayOf(H1, H2, H3, ORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE),
113+
conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, ORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE),
99114
),
100115
ORDERED_LIST to StylesMergingConfig(
101-
conflictingStyles = arrayOf(H1, H2, H3, UNORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE),
116+
conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, UNORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE),
102117
),
103118
LINK to StylesMergingConfig(
104119
blockingStyles = arrayOf(INLINE_CODE, CODE_BLOCK, MENTION)

android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ class HtmlStyle {
2828
var h3FontSize: Int = 56
2929
var h3Bold: Boolean = false
3030

31+
var h4FontSize: Int = 48
32+
var h4Bold: Boolean = false
33+
34+
var h5FontSize: Int = 40
35+
var h5Bold: Boolean = false
36+
37+
var h6FontSize: Int = 32
38+
var h6Bold: Boolean = false
39+
3140
var blockquoteColor: Int? = null
3241
var blockquoteBorderColor: Int = Color.BLACK
3342
var blockquoteStripeWidth: Int = 2
@@ -80,6 +89,18 @@ class HtmlStyle {
8089
h3FontSize = parseFloat(h3Style, "fontSize").toInt()
8190
h3Bold = h3Style?.getBoolean("bold") == true
8291

92+
val h4Style = style.getMap("h4")
93+
h4FontSize = parseFloat(h4Style, "fontSize").toInt()
94+
h4Bold = h4Style?.getBoolean("bold") == true
95+
96+
val h5Style = style.getMap("h5")
97+
h5FontSize = parseFloat(h5Style, "fontSize").toInt()
98+
h5Bold = h5Style?.getBoolean("bold") == true
99+
100+
val h6Style = style.getMap("h6")
101+
h6FontSize = parseFloat(h6Style, "fontSize").toInt()
102+
h6Bold = h6Style?.getBoolean("bold") == true
103+
83104
val blockquoteStyle = style.getMap("blockquote")
84105
blockquoteColor = parseOptionalColor(blockquoteStyle, "color")
85106
blockquoteBorderColor = parseColor(blockquoteStyle, "borderColor")

android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
import com.swmansion.enriched.spans.EnrichedH1Span;
1717
import com.swmansion.enriched.spans.EnrichedH2Span;
1818
import com.swmansion.enriched.spans.EnrichedH3Span;
19+
import com.swmansion.enriched.spans.EnrichedH4Span;
20+
import com.swmansion.enriched.spans.EnrichedH5Span;
21+
import com.swmansion.enriched.spans.EnrichedH6Span;
1922
import com.swmansion.enriched.spans.EnrichedImageSpan;
2023
import com.swmansion.enriched.spans.EnrichedInlineCodeSpan;
2124
import com.swmansion.enriched.spans.EnrichedItalicSpan;
@@ -153,6 +156,12 @@ private static String getBlockTag(EnrichedParagraphSpan[] spans) {
153156
return "h2";
154157
} else if (span instanceof EnrichedH3Span) {
155158
return "h3";
159+
} else if (span instanceof EnrichedH4Span) {
160+
return "h4";
161+
} else if (span instanceof EnrichedH5Span) {
162+
return "h5";
163+
} else if (span instanceof EnrichedH6Span) {
164+
return "h6";
156165
}
157166
}
158167

@@ -441,6 +450,12 @@ private void handleStartTag(String tag, Attributes attributes) {
441450
startHeading(mSpannableStringBuilder, 2);
442451
} else if (tag.equalsIgnoreCase("h3")) {
443452
startHeading(mSpannableStringBuilder, 3);
453+
} else if (tag.equalsIgnoreCase("h4")) {
454+
startHeading(mSpannableStringBuilder, 4);
455+
} else if (tag.equalsIgnoreCase("h5")) {
456+
startHeading(mSpannableStringBuilder, 5);
457+
} else if (tag.equalsIgnoreCase("h6")) {
458+
startHeading(mSpannableStringBuilder, 6);
444459
} else if (tag.equalsIgnoreCase("img")) {
445460
startImg(mSpannableStringBuilder, attributes, mImageGetter, mStyle);
446461
} else if (tag.equalsIgnoreCase("code")) {
@@ -479,6 +494,12 @@ private void handleEndTag(String tag) {
479494
endHeading(mSpannableStringBuilder, mStyle, 2);
480495
} else if (tag.equalsIgnoreCase("h3")) {
481496
endHeading(mSpannableStringBuilder, mStyle, 3);
497+
} else if (tag.equalsIgnoreCase("h4")) {
498+
endHeading(mSpannableStringBuilder, mStyle, 4);
499+
} else if (tag.equalsIgnoreCase("h5")) {
500+
endHeading(mSpannableStringBuilder, mStyle, 5);
501+
} else if (tag.equalsIgnoreCase("h6")) {
502+
endHeading(mSpannableStringBuilder, mStyle, 6);
482503
} else if (tag.equalsIgnoreCase("code")) {
483504
end(mSpannableStringBuilder, Code.class, new EnrichedInlineCodeSpan(mStyle));
484505
} else if (tag.equalsIgnoreCase("mention")) {
@@ -582,6 +603,15 @@ private void startHeading(Editable text, int level) {
582603
case 3:
583604
start(text, new H3());
584605
break;
606+
case 4:
607+
start(text, new H4());
608+
break;
609+
case 5:
610+
start(text, new H5());
611+
break;
612+
case 6:
613+
start(text, new H6());
614+
break;
585615
default:
586616
throw new IllegalArgumentException("Unsupported heading level: " + level);
587617
}
@@ -603,6 +633,18 @@ private static void endHeading(Editable text, HtmlStyle style, int level) {
603633
H3 lastH3 = getLast(text, H3.class);
604634
setParagraphSpanFromMark(text, lastH3, new EnrichedH3Span(style));
605635
break;
636+
case 4:
637+
H4 lastH4 = getLast(text, H4.class);
638+
setParagraphSpanFromMark(text, lastH4, new EnrichedH4Span(style));
639+
break;
640+
case 5:
641+
H5 lastH5 = getLast(text, H5.class);
642+
setParagraphSpanFromMark(text, lastH5, new EnrichedH5Span(style));
643+
break;
644+
case 6:
645+
H6 lastH6 = getLast(text, H6.class);
646+
setParagraphSpanFromMark(text, lastH6, new EnrichedH6Span(style));
647+
break;
606648
default:
607649
throw new IllegalArgumentException("Unsupported heading level: " + level);
608650
}
@@ -788,6 +830,15 @@ private static class H2 {
788830
private static class H3 {
789831
}
790832

833+
private static class H4 {
834+
}
835+
836+
private static class H5 {
837+
}
838+
839+
private static class H6 {
840+
}
841+
791842
private static class Bold {
792843
}
793844

android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) {
2727
private set
2828
var h3Start: Int? = null
2929
private set
30+
var h4Start: Int? = null
31+
private set
32+
var h5Start: Int? = null
33+
private set
34+
var h6Start: Int? = null
35+
private set
3036
var codeBlockStart: Int? = null
3137
private set
3238
var blockQuoteStart: Int? = null
@@ -82,6 +88,21 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) {
8288
emitStateChangeEvent()
8389
}
8490

91+
fun setH4Start(start: Int?) {
92+
this.h4Start = start
93+
emitStateChangeEvent()
94+
}
95+
96+
fun setH5Start(start: Int?) {
97+
this.h5Start = start
98+
emitStateChangeEvent()
99+
}
100+
101+
fun setH6Start(start: Int?) {
102+
this.h6Start = start
103+
emitStateChangeEvent()
104+
}
105+
85106
fun setCodeBlockStart(start: Int?) {
86107
this.codeBlockStart = start
87108
emitStateChangeEvent()
@@ -127,6 +148,9 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) {
127148
EnrichedSpans.H1 -> h1Start
128149
EnrichedSpans.H2 -> h2Start
129150
EnrichedSpans.H3 -> h3Start
151+
EnrichedSpans.H4 -> h4Start
152+
EnrichedSpans.H5 -> h5Start
153+
EnrichedSpans.H6 -> h6Start
130154
EnrichedSpans.CODE_BLOCK -> codeBlockStart
131155
EnrichedSpans.BLOCK_QUOTE -> blockQuoteStart
132156
EnrichedSpans.ORDERED_LIST -> orderedListStart
@@ -150,6 +174,9 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) {
150174
EnrichedSpans.H1 -> setH1Start(start)
151175
EnrichedSpans.H2 -> setH2Start(start)
152176
EnrichedSpans.H3 -> setH3Start(start)
177+
EnrichedSpans.H4 -> setH4Start(start)
178+
EnrichedSpans.H5 -> setH5Start(start)
179+
EnrichedSpans.H6 -> setH6Start(start)
153180
EnrichedSpans.CODE_BLOCK -> setCodeBlockStart(start)
154181
EnrichedSpans.BLOCK_QUOTE -> setBlockQuoteStart(start)
155182
EnrichedSpans.ORDERED_LIST -> setOrderedListStart(start)
@@ -170,6 +197,9 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) {
170197
payload.putBoolean("isH1", h1Start != null)
171198
payload.putBoolean("isH2", h2Start != null)
172199
payload.putBoolean("isH3", h3Start != null)
200+
payload.putBoolean("isH4", h4Start != null)
201+
payload.putBoolean("isH5", h5Start != null)
202+
payload.putBoolean("isH6", h6Start != null)
173203
payload.putBoolean("isCodeBlock", codeBlockStart != null)
174204
payload.putBoolean("isBlockQuote", blockQuoteStart != null)
175205
payload.putBoolean("isOrderedList", orderedListStart != null)

0 commit comments

Comments
 (0)