Skip to content

Commit 4dedbd5

Browse files
authored
Merge pull request #966 from wordpress-mobile/feature/item-indent
List indent and outdent
2 parents e331b7b + 4f42371 commit 4dedbd5

21 files changed

+980
-20
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,26 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
957957
selectedStyles.addAll(styles)
958958
}
959959

960+
fun indent() {
961+
history.beforeTextChanged(this@AztecText)
962+
blockFormatter.indent()
963+
contentChangeWatcher.notifyContentChanged()
964+
}
965+
966+
fun outdent() {
967+
history.beforeTextChanged(this@AztecText)
968+
blockFormatter.outdent()
969+
contentChangeWatcher.notifyContentChanged()
970+
}
971+
972+
fun isIndentAvailable(): Boolean {
973+
return blockFormatter.isIndentAvailable()
974+
}
975+
976+
fun isOutdentAvailable(): Boolean {
977+
return blockFormatter.isOutdentAvailable()
978+
}
979+
960980
fun setOnSelectionChangedListener(onSelectionChangedListener: OnSelectionChangedListener) {
961981
this.onSelectionChangedListener = onSelectionChangedListener
962982
}

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

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,26 +51,45 @@ class BlockFormatter(editor: AztecText,
5151
private val alignmentRendering: AlignmentRendering,
5252
private val exclusiveBlockStyles: ExclusiveBlockStyles
5353
) : AztecFormatter(editor) {
54+
private val listFormatter = ListFormatter(editor)
55+
5456
data class ListStyle(val indicatorColor: Int, val indicatorMargin: Int, val indicatorPadding: Int, val indicatorWidth: Int, val verticalPadding: Int) {
5557
fun leadingMargin(): Int {
5658
return indicatorMargin + 2 * indicatorWidth + indicatorPadding
5759
}
5860
}
59-
6061
data class QuoteStyle(val quoteBackground: Int, val quoteColor: Int, val quoteBackgroundAlpha: Float, val quoteMargin: Int, val quotePadding: Int, val quoteWidth: Int, val verticalPadding: Int)
6162
data class PreformatStyle(val preformatBackground: Int, val preformatBackgroundAlpha: Float, val preformatColor: Int, val verticalPadding: Int)
6263
data class HeaderStyle(val verticalPadding: Int)
6364
data class ExclusiveBlockStyles(val enabled: Boolean = false)
6465

66+
fun indent() {
67+
if (listFormatter.indentList()) return
68+
// TODO handle other indents
69+
}
70+
71+
fun outdent() {
72+
if (listFormatter.outdentList()) return
73+
// TODO handle other outdents
74+
}
75+
76+
fun isIndentAvailable(): Boolean {
77+
return listFormatter.isIndentAvailable()
78+
}
79+
80+
fun isOutdentAvailable(): Boolean {
81+
return listFormatter.isOutdentAvailable()
82+
}
83+
6584
fun toggleOrderedList() {
66-
if (!containsList(AztecTextFormat.FORMAT_ORDERED_LIST, 0)) {
67-
if (containsList(AztecTextFormat.FORMAT_UNORDERED_LIST, 0) || containsList(AztecTextFormat.FORMAT_TASK_LIST, 0)) {
85+
if (!containsList(listClass = AztecTextFormat.FORMAT_ORDERED_LIST.toBlockSpanClass())) {
86+
if (containsList(listClass = AztecTextFormat.FORMAT_UNORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_TASK_LIST.toBlockSpanClass())) {
6887
switchListType(AztecTextFormat.FORMAT_ORDERED_LIST)
6988
} else {
7089
applyBlockStyle(AztecTextFormat.FORMAT_ORDERED_LIST)
7190
}
7291
} else {
73-
if (containsList(AztecTextFormat.FORMAT_UNORDERED_LIST, 0) || containsList(AztecTextFormat.FORMAT_TASK_LIST, 0)) {
92+
if (containsList(listClass = AztecTextFormat.FORMAT_UNORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_TASK_LIST.toBlockSpanClass())) {
7493
switchListType(AztecTextFormat.FORMAT_ORDERED_LIST)
7594
} else {
7695
removeBlockStyle(AztecTextFormat.FORMAT_ORDERED_LIST)
@@ -79,14 +98,14 @@ class BlockFormatter(editor: AztecText,
7998
}
8099

81100
fun toggleUnorderedList() {
82-
if (!containsList(AztecTextFormat.FORMAT_UNORDERED_LIST, 0)) {
83-
if (containsList(AztecTextFormat.FORMAT_ORDERED_LIST, 0) || containsList(AztecTextFormat.FORMAT_TASK_LIST, 0)) {
101+
if (!containsList(listClass = AztecTextFormat.FORMAT_UNORDERED_LIST.toBlockSpanClass())) {
102+
if (containsList(listClass = AztecTextFormat.FORMAT_ORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_TASK_LIST.toBlockSpanClass())) {
84103
switchListType(AztecTextFormat.FORMAT_UNORDERED_LIST)
85104
} else {
86105
applyBlockStyle(AztecTextFormat.FORMAT_UNORDERED_LIST)
87106
}
88107
} else {
89-
if (containsList(AztecTextFormat.FORMAT_ORDERED_LIST, 0) || containsList(AztecTextFormat.FORMAT_TASK_LIST, 0)) {
108+
if (containsList(listClass = AztecTextFormat.FORMAT_ORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_TASK_LIST.toBlockSpanClass())) {
90109
switchListType(AztecTextFormat.FORMAT_UNORDERED_LIST)
91110
} else {
92111
removeBlockStyle(AztecTextFormat.FORMAT_UNORDERED_LIST)
@@ -95,16 +114,16 @@ class BlockFormatter(editor: AztecText,
95114
}
96115

97116
fun toggleTaskList() {
98-
if (!containsList(AztecTextFormat.FORMAT_TASK_LIST, 0)) {
99-
if (containsList(AztecTextFormat.FORMAT_ORDERED_LIST, 0) || containsList(AztecTextFormat.FORMAT_UNORDERED_LIST, 0)) {
117+
if (!containsList(listClass = AztecTextFormat.FORMAT_TASK_LIST.toBlockSpanClass())) {
118+
if (containsList(listClass = AztecTextFormat.FORMAT_ORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_UNORDERED_LIST.toBlockSpanClass())) {
100119
switchListType(AztecTextFormat.FORMAT_TASK_LIST)
101120
editor.addRefreshListenersToTaskLists(editableText, selectionStart, selectionEnd)
102121
} else {
103122
applyBlockStyle(AztecTextFormat.FORMAT_TASK_LIST)
104123
editor.addRefreshListenersToTaskLists(editableText, selectionStart, selectionEnd)
105124
}
106125
} else {
107-
if (containsList(AztecTextFormat.FORMAT_ORDERED_LIST, 0) || containsList(AztecTextFormat.FORMAT_UNORDERED_LIST, 0)) {
126+
if (containsList(listClass = AztecTextFormat.FORMAT_ORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_UNORDERED_LIST.toBlockSpanClass())) {
108127
switchListType(AztecTextFormat.FORMAT_TASK_LIST)
109128
editor.addRefreshListenersToTaskLists(editableText, selectionStart, selectionEnd)
110129
} else {
@@ -470,6 +489,23 @@ class BlockFormatter(editor: AztecText,
470489
}
471490
}
472491

492+
private fun ITextFormat.toBlockSpanClass(): Class<*> {
493+
return when (this) {
494+
AztecTextFormat.FORMAT_ORDERED_LIST -> AztecOrderedListSpan::class.java
495+
AztecTextFormat.FORMAT_UNORDERED_LIST -> AztecUnorderedListSpan::class.java
496+
AztecTextFormat.FORMAT_TASK_LIST -> AztecTaskListSpan::class.java
497+
AztecTextFormat.FORMAT_QUOTE -> AztecQuoteSpan::class.java
498+
AztecTextFormat.FORMAT_HEADING_1,
499+
AztecTextFormat.FORMAT_HEADING_2,
500+
AztecTextFormat.FORMAT_HEADING_3,
501+
AztecTextFormat.FORMAT_HEADING_4,
502+
AztecTextFormat.FORMAT_HEADING_5,
503+
AztecTextFormat.FORMAT_HEADING_6 -> AztecHeadingSpan::class.java
504+
AztecTextFormat.FORMAT_PREFORMAT -> AztecPreformatSpan::class.java
505+
else -> ParagraphSpan::class.java
506+
}
507+
}
508+
473509
private fun <T : KClass<out IAztecBlockSpan>> makeBlockSpan(type: T, textFormat: ITextFormat, nestingLevel: Int, attrs: AztecAttributes = AztecAttributes()): IAztecBlockSpan {
474510
val typeIsAssignableTo = { clazz: KClass<out Any> -> clazz.java.isAssignableFrom(type.java) }
475511
return when {
@@ -871,12 +907,16 @@ class BlockFormatter(editor: AztecText,
871907
}
872908
}
873909

874-
fun containsList(textFormat: ITextFormat, nestingLevel: Int, selStart: Int = selectionStart, selEnd: Int = selectionEnd): Boolean {
910+
fun containsList(format: ITextFormat, selStart: Int = selectionStart, selEnd: Int = selectionEnd): Boolean {
911+
return containsList(selStart, selEnd, format.toBlockSpanClass())
912+
}
913+
914+
fun containsList(selStart: Int = selectionStart, selEnd: Int = selectionEnd, listClass: Class<*>): Boolean {
875915
val lines = TextUtils.split(editableText.toString(), "\n")
876916
val list = ArrayList<Int>()
877917

878918
for (i in lines.indices) {
879-
val lineStart = (0..i - 1).sumBy { lines[it].length + 1 }
919+
val lineStart = (0 until i).sumBy { lines[it].length + 1 }
880920
val lineEnd = lineStart + lines[i].length
881921

882922
if (lineStart > lineEnd) {
@@ -892,31 +932,31 @@ class BlockFormatter(editor: AztecText,
892932
* multiple lines (before), current partially or entirely selected
893933
*/
894934
if ((lineStart >= selStart && selEnd >= lineEnd)
895-
|| (lineStart <= selEnd && selEnd <= lineEnd)
896-
|| (lineStart <= selStart && selStart <= lineEnd)) {
935+
|| (selEnd in lineStart..lineEnd)
936+
|| (selStart in lineStart..lineEnd)) {
897937
list.add(i)
898938
}
899939
}
900940

901941
if (list.isEmpty()) return false
902942

903-
return list.any { containsBlockElement(textFormat, it, editableText, nestingLevel) }
943+
return list.any { containsBlockElement(it, editableText, listClass) }
904944
}
905945

906-
fun containsBlockElement(textFormat: ITextFormat, index: Int, text: Editable, nestingLevel: Int): Boolean {
946+
fun containsBlockElement(index: Int, text: Editable, blockClass: Class<*>): Boolean {
907947
val lines = TextUtils.split(text.toString(), "\n")
908948
if (index < 0 || index >= lines.size) {
909949
return false
910950
}
911951

912-
val start = (0..index - 1).sumBy { lines[it].length + 1 }
952+
val start = (0 until index).sumBy { lines[it].length + 1 }
913953
val end = start + lines[index].length
914954

915955
if (start > end) {
916956
return false
917957
}
918958

919-
val spans = editableText.getSpans(start, end, makeBlockSpan(textFormat, nestingLevel).javaClass)
959+
val spans = editableText.getSpans(start, end, blockClass)
920960
return spans.isNotEmpty()
921961
}
922962

0 commit comments

Comments
 (0)