Skip to content

Commit f1a9a35

Browse files
committed
Add methods to check whether indent/outdent is available on the current list item
1 parent 9ef279c commit f1a9a35

File tree

5 files changed

+108
-4
lines changed

5 files changed

+108
-4
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,14 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
954954
contentChangeWatcher.notifyContentChanged()
955955
}
956956

957+
fun isIndentAvailable(): Boolean {
958+
return blockFormatter.isIndentAvailable()
959+
}
960+
961+
fun isOutdentAvailable(): Boolean {
962+
return blockFormatter.isOutdentAvailable()
963+
}
964+
957965
fun setOnSelectionChangedListener(onSelectionChangedListener: OnSelectionChangedListener) {
958966
this.onSelectionChangedListener = onSelectionChangedListener
959967
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ class BlockFormatter(editor: AztecText,
7171
// TODO handle other outdents
7272
}
7373

74+
fun isIndentAvailable(): Boolean {
75+
return listFormatter.isIndentAvailable()
76+
}
77+
78+
fun isOutdentAvailable(): Boolean {
79+
return listFormatter.isOutdentAvailable()
80+
}
81+
7482
fun toggleOrderedList() {
7583
if (!containsList(listClass = AztecTextFormat.FORMAT_ORDERED_LIST.toBlockSpanClass())) {
7684
if (containsList(listClass = AztecTextFormat.FORMAT_UNORDERED_LIST.toBlockSpanClass()) || containsList(listClass = AztecTextFormat.FORMAT_TASK_LIST.toBlockSpanClass())) {

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@ class ListFormatter(editor: AztecText) : AztecFormatter(editor) {
6363
return true
6464
}
6565

66+
/**
67+
* Returns true if the current position contains a list item that can be indented. It contains a copy of logic
68+
* from `indentList` function without the actual list idents.
69+
*/
70+
fun isIndentAvailable(selStart: Int = selectionStart, selEnd: Int = selectionEnd): Boolean {
71+
val listSpans = editableText.getSpans(selStart, selEnd, AztecListSpan::class.java).filterCorrectSpans(selectionStart = selStart, selectionEnd = selEnd)
72+
if (listSpans.isEmpty()) return false
73+
buildListState(listSpans, selStart, selEnd)?.apply {
74+
if (listItemSpanBeforeSelection == null) {
75+
return false
76+
}
77+
return if (listItemSpanBeforeSelection.nestingLevel == nestingLevel) {
78+
if ((listItemSpanAfterSelection == null || listItemSpanAfterSelection.nestingLevel <= nestingLevel)) {
79+
true
80+
} else listSpanAfterSelection != null && listSpanAfterSelection.nestingLevel > nestingLevel
81+
} else if (deeperListSpanBeforeSelection?.nestingLevel == nestingLevel + 1) {
82+
if ((listItemSpanAfterSelection == null || listItemSpanAfterSelection.nestingLevel <= nestingLevel)) {
83+
true
84+
} else listItemSpanAfterSelection.nestingLevel == nestingLevel + 2
85+
} else {
86+
false
87+
}
88+
}
89+
return false
90+
}
91+
6692
private fun AztecListSpan.copyList(increaseNestingLevel: Boolean = false): AztecListSpan? {
6793
val updatedNestingLevel = if (increaseNestingLevel) nestingLevel + 2 else nestingLevel
6894
return when (this) {
@@ -155,6 +181,41 @@ class ListFormatter(editor: AztecText) : AztecFormatter(editor) {
155181
return true
156182
}
157183

184+
/**
185+
* Returns true if the current position contains a list item that can be outdented. It contains a copy of logic
186+
* from `outdentList` function without the actual list idents.
187+
*/
188+
fun isOutdentAvailable(selStart: Int = selectionStart, selEnd: Int = selectionEnd): Boolean {
189+
val listSpans = editableText.getSpans(selStart, selEnd, AztecListSpan::class.java).filterCorrectSpans(selectionStart = selStart, selectionEnd = selEnd)
190+
if (listSpans.isEmpty()) return false
191+
buildListState(listSpans, selStart, selEnd)?.apply {
192+
when {
193+
listItemSpanBeforeSelection == null && listItemSpanAfterSelection == null -> {
194+
return true
195+
}
196+
listItemSpanBeforeSelection == null && listItemSpanAfterSelection != null -> {
197+
if (listItemSpanAfterSelection.nestingLevel == nestingLevel) {
198+
return true
199+
}
200+
}
201+
listItemSpanBeforeSelection != null && listItemSpanAfterSelection == null -> {
202+
return true
203+
}
204+
listItemSpanBeforeSelection != null && listItemSpanAfterSelection != null -> {
205+
return if (listItemSpanBeforeSelection.nestingLevel >= nestingLevel) {
206+
if (listItemSpanAfterSelection.nestingLevel == nestingLevel) {
207+
true
208+
} else listItemSpanAfterSelection.nestingLevel < nestingLevel
209+
} else if (listItemSpanBeforeSelection.nestingLevel < nestingLevel && listItemSpanAfterSelection.nestingLevel == nestingLevel) {
210+
true
211+
} else listItemSpanBeforeSelection.nestingLevel < nestingLevel && listItemSpanAfterSelection.nestingLevel < nestingLevel
212+
}
213+
else -> return false
214+
}
215+
}
216+
return true
217+
}
218+
158219
private data class ListState(
159220
val nestingLevel: Int,
160221
val directParentList: AztecListSpan,

aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,16 @@ class AztecToolbar : FrameLayout, IAztecToolbar, OnMenuItemClickListener {
538538
selectHeadingMenuItem(appliedStyles)
539539
selectListMenuItem(appliedStyles)
540540
highlightAlignButtons(appliedStyles)
541+
setIndentState()
542+
}
543+
544+
private fun setIndentState() {
545+
findViewById<View>(ToolbarAction.INDENT.buttonId)?.let {
546+
toggleButtonState(it, editor?.isIndentAvailable() == true)
547+
}
548+
findViewById<View>(ToolbarAction.OUTDENT.buttonId)?.let {
549+
toggleButtonState(it, editor?.isOutdentAvailable() == true)
550+
}
541551
}
542552

543553
private fun highlightAlignButtons(appliedStyles: ArrayList<ITextFormat>) {

aztec/src/test/kotlin/org/wordpress/aztec/ListIndentTest.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
2525
val listType = listTextFormat
2626
val listTag = listHtmlTag
2727

28-
val otherListType = if (listTextFormat == AztecTextFormat.FORMAT_ORDERED_LIST) AztecTextFormat.FORMAT_UNORDERED_LIST
29-
else AztecTextFormat.FORMAT_ORDERED_LIST
30-
val otherListTag = if (listTag == "ol") "ul" else "ol"
31-
3228
lateinit var editText: AztecText
3329
lateinit var menuList: PopupMenu
3430
lateinit var menuListOrdered: MenuItem
@@ -81,6 +77,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
8177
fun indentLastItemOfAListIfNoOtherItemsIndented() {
8278
editText.fromHtml("<$listTag><li>Item 1</li><li>Item 2</li></$listTag>")
8379
editText.setSelection(editText.editableText.indexOf("2"))
80+
Assert.assertEquals(editText.isIndentAvailable(), true)
8481
editText.indent()
8582

8683
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li></$listTag>", editText.toHtml())
@@ -91,6 +88,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
9188
fun outdentLastItemOfAListIfNoOtherItemsIndented() {
9289
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li></$listTag>")
9390
editText.setSelection(editText.editableText.indexOf("2"))
91+
Assert.assertEquals(editText.isOutdentAvailable(), true)
9492
editText.outdent()
9593

9694
Assert.assertEquals("<$listTag><li>Item 1</li><li>Item 2</li></$listTag>", editText.toHtml())
@@ -101,6 +99,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
10199
fun indentMiddleItemOfAListIfNoOtherItemsIndented() {
102100
editText.fromHtml("<$listTag><li>Item 1</li><li>Item 2</li><li>Item 3</li></$listTag>")
103101
editText.setSelection(editText.editableText.indexOf("2"))
102+
Assert.assertEquals(editText.isIndentAvailable(), true)
104103
editText.indent()
105104

106105
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li><li>Item 3</li></$listTag>", editText.toHtml())
@@ -111,6 +110,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
111110
fun outdentMiddleItemOfAListIfNoOtherItemsIndented() {
112111
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li><li>Item 3</li></$listTag>")
113112
editText.setSelection(editText.editableText.indexOf("2"))
113+
Assert.assertEquals(editText.isOutdentAvailable(), true)
114114
editText.outdent()
115115

116116
Assert.assertEquals("<$listTag><li>Item 1</li><li>Item 2</li><li>Item 3</li></$listTag>", editText.toHtml())
@@ -121,6 +121,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
121121
fun indentMiddleItemOfAListIfFollowingItemIndented() {
122122
editText.fromHtml("<$listTag><li>Item 1</li><li>Item 2<$listTag style=\"text-align:left;\"><li>Item 3</li></$listTag></li></$listTag>")
123123
editText.setSelection(editText.editableText.indexOf("2"))
124+
Assert.assertEquals(editText.isIndentAvailable(), true)
124125
editText.indent()
125126

126127
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>", editText.toHtml())
@@ -131,6 +132,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
131132
fun outdentMiddleItemOfAListIfFollowingItemIndented() {
132133
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>")
133134
editText.setSelection(editText.editableText.indexOf("2"))
135+
Assert.assertEquals(editText.isOutdentAvailable(), true)
134136
editText.outdent()
135137

136138
Assert.assertEquals("<$listTag><li>Item 1</li><li>Item 2<$listTag style=\"text-align:left;\"><li>Item 3</li></$listTag></li></$listTag>", editText.toHtml())
@@ -141,6 +143,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
141143
fun indentLastItemOfAListIfPreviousItemIndented() {
142144
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li><li>Item 3</li></$listTag>")
143145
editText.setSelection(editText.editableText.indexOf("3"))
146+
Assert.assertEquals(editText.isIndentAvailable(), true)
144147
editText.indent()
145148

146149
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>", editText.toHtml())
@@ -151,6 +154,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
151154
fun outdentLastItemOfAListIfPreviousItemIndented() {
152155
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>")
153156
editText.setSelection(editText.editableText.indexOf("3"))
157+
Assert.assertEquals(editText.isOutdentAvailable(), true)
154158
editText.outdent()
155159

156160
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li><li>Item 3</li></$listTag>", editText.toHtml())
@@ -161,6 +165,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
161165
fun indentItemInAMiddleOfOtherIndentedItems() {
162166
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li><li>Item 3<$listTag style=\"text-align:left;\"><li>Item 4</li></$listTag></li></$listTag>")
163167
editText.setSelection(editText.editableText.indexOf("3"))
168+
Assert.assertEquals(editText.isIndentAvailable(), true)
164169
editText.indent()
165170

166171
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li><li>Item 4</li></$listTag></li></$listTag>", editText.toHtml())
@@ -171,6 +176,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
171176
fun outdentItemInAMiddleOfOtherIndentedItems() {
172177
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li><li>Item 4</li></$listTag></li></$listTag>")
173178
editText.setSelection(editText.editableText.indexOf("3"))
179+
Assert.assertEquals(editText.isOutdentAvailable(), true)
174180
editText.outdent()
175181

176182
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li></$listTag></li><li>Item 3<$listTag style=\"text-align:left;\"><li>Item 4</li></$listTag></li></$listTag>", editText.toHtml())
@@ -181,6 +187,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
181187
fun indentItemInAMiddleOfOtherNestedIndentedItems() {
182188
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2<$listTag style=\"text-align:left;\"><li>Item 3</li></$listTag></li><li>Item 4<$listTag style=\"text-align:left;\"><li>Item 5</li></$listTag></li></$listTag></$listTag></li>")
183189
editText.setSelection(editText.editableText.indexOf("4"))
190+
Assert.assertEquals(editText.isIndentAvailable(), true)
184191
editText.indent()
185192

186193
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2<$listTag style=\"text-align:left;\"><li>Item 3</li><li>Item 4</li><li>Item 5</li></$listTag></li></$listTag></li></$listTag>", editText.toHtml())
@@ -191,6 +198,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
191198
fun outdentItemInAMiddleOfOtherNestedIndentedItems() {
192199
editText.fromHtml("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2<$listTag style=\"text-align:left;\"><li>Item 3</li><li>Item 4</li><li>Item 5</li></$listTag></li></$listTag></li></$listTag>")
193200
editText.setSelection(editText.editableText.indexOf("4"))
201+
Assert.assertEquals(editText.isOutdentAvailable(), true)
194202
editText.outdent()
195203

196204
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2<$listTag style=\"text-align:left;\"><li>Item 3</li></$listTag></li><li>Item 4<$listTag style=\"text-align:left;\"><li>Item 5</li></$listTag></li></$listTag></li></$listTag>", editText.toHtml())
@@ -201,6 +209,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
201209
fun outdentSplitsListInTheMiddle() {
202210
editText.fromHtml("<$listTag><li>Item 1</li><li>Item 2</li><li>Item 3</li></$listTag>")
203211
editText.setSelection(editText.editableText.indexOf("2"))
212+
Assert.assertEquals(editText.isOutdentAvailable(), true)
204213
editText.outdent()
205214

206215
Assert.assertEquals("<$listTag style=\"text-align:left;\"><li>Item 1</li></$listTag>Item 2<$listTag><li>Item 3</li></$listTag>", editText.toHtml())
@@ -211,6 +220,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
211220
fun indentMultipleItems() {
212221
editText.fromHtml("<$listTag><li>Item 1</li><li>Item 2</li><li>Item 3</li></$listTag>")
213222
editText.setSelection(editText.editableText.indexOf("2"), editText.editableText.indexOf("3"))
223+
Assert.assertEquals(editText.isIndentAvailable(), true)
214224
editText.indent()
215225

216226
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>", editText.toHtml())
@@ -221,6 +231,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
221231
fun outdentMultipleItems() {
222232
editText.fromHtml("<$listTag><li>Item 1<$listTag><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>")
223233
editText.setSelection(editText.editableText.indexOf("2"), editText.editableText.indexOf("3"))
234+
Assert.assertEquals(editText.isOutdentAvailable(), true)
224235
editText.outdent()
225236

226237
Assert.assertEquals("<$listTag><li>Item 1</li><li>Item 2</li><li>Item 3</li></$listTag>", editText.toHtml())
@@ -231,6 +242,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
231242
fun indentMultipleItemsInALongerList() {
232243
editText.fromHtml("<$listTag><li>Item 1</li><li>Item 2</li><li>Item 3</li><li>Item 4</li></$listTag>")
233244
editText.setSelection(editText.editableText.indexOf("2"), editText.editableText.indexOf("3"))
245+
Assert.assertEquals(editText.isIndentAvailable(), true)
234246
editText.indent()
235247

236248
Assert.assertEquals("<$listTag><li>Item 1<$listTag style=\"text-align:left;\"><li>Item 2</li><li>Item 3</li></$listTag></li><li>Item 4</li></$listTag>", editText.toHtml())
@@ -242,6 +254,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
242254
val source = "<$listTag><li>Item 1</li><li>Item 2<$listTag><li>Item 3</li></$listTag></li></$listTag>"
243255
editText.fromHtml(source)
244256
editText.setSelection(editText.editableText.indexOf("2"), editText.editableText.indexOf("3"))
257+
Assert.assertEquals(editText.isIndentAvailable(), false)
245258
editText.indent()
246259

247260
Assert.assertEquals(source, editText.toHtml())
@@ -253,6 +266,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
253266
val source = "<$listTag><li>Item 1</li><li>Item 2<$listTag><li>Item 3</li></$listTag></li></$listTag>"
254267
editText.fromHtml(source)
255268
editText.setSelection(editText.editableText.indexOf("2"), editText.editableText.indexOf("3"))
269+
Assert.assertEquals(editText.isOutdentAvailable(), true)
256270
editText.outdent()
257271

258272
Assert.assertEquals(source, editText.toHtml())
@@ -264,6 +278,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
264278
val source = "<$listTag><li>Item 1</li><li>Item 2</li></$listTag>"
265279
editText.fromHtml(source)
266280
editText.setSelection(editText.editableText.indexOf("1"))
281+
Assert.assertEquals(editText.isIndentAvailable(), false)
267282
editText.indent()
268283

269284
Assert.assertEquals(source, editText.toHtml())
@@ -275,6 +290,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
275290
val source = "<$listTag><li>Item 1<$listTag><li>Item 2</li><li>Item 3</li></$listTag></li></$listTag>"
276291
editText.fromHtml(source)
277292
editText.setSelection(editText.editableText.indexOf("2"))
293+
Assert.assertEquals(editText.isIndentAvailable(), false)
278294
editText.indent()
279295

280296
Assert.assertEquals(source, editText.toHtml())
@@ -286,6 +302,7 @@ class ListIndentTest(listTextFormat: ITextFormat, listHtmlTag: String) {
286302
val source = "<$listTag><li>Item 1<$listTag><li>Item 2<$listTag><li>Item 3</li><li>Item 4</li></$listTag></li></$listTag></li></$listTag>"
287303
editText.fromHtml(source)
288304
editText.setSelection(editText.editableText.indexOf("3"))
305+
Assert.assertEquals(editText.isIndentAvailable(), false)
289306
editText.indent()
290307

291308
Assert.assertEquals(source, editText.toHtml())

0 commit comments

Comments
 (0)