Skip to content

Commit 6a0b06c

Browse files
committed
Make sure images and horizontal rule are only added after the currently selected blcok
1 parent e9c357d commit 6a0b06c

File tree

4 files changed

+213
-97
lines changed

4 files changed

+213
-97
lines changed

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import org.wordpress.aztec.formatting.BlockFormatter
6868
import org.wordpress.aztec.formatting.InlineFormatter
6969
import org.wordpress.aztec.formatting.LineBlockFormatter
7070
import org.wordpress.aztec.formatting.LinkFormatter
71+
import org.wordpress.aztec.formatting.MediaFormatter
7172
import org.wordpress.aztec.handlers.HeadingHandler
7273
import org.wordpress.aztec.handlers.ListHandler
7374
import org.wordpress.aztec.handlers.ListItemHandler
@@ -254,6 +255,9 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
254255
var isInCalypsoMode = true
255256
var isInGutenbergMode: Boolean = false
256257
val alignmentRendering: AlignmentRendering
258+
// If this field is true, the media and horizontal line are added inline. If it's false, they are added after the
259+
// current block.
260+
var shouldAddMediaInline: Boolean = true
257261

258262
var consumeHistoryEvent: Boolean = false
259263

@@ -276,6 +280,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
276280
lateinit var blockFormatter: BlockFormatter
277281
lateinit var lineBlockFormatter: LineBlockFormatter
278282
lateinit var linkFormatter: LinkFormatter
283+
lateinit var mediaFormatter: MediaFormatter
279284

280285
var imageGetter: Html.ImageGetter? = null
281286
var videoThumbnailGetter: Html.VideoThumbnailGetter? = null
@@ -369,6 +374,15 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
369374
isInGutenbergMode = isCompatibleWithGutenberg
370375
}
371376

377+
/**
378+
* Default behaviour is to add media and horizontal rule inline. They could be added inside lists and quotes.
379+
* If you call this method, this behaviour will be disabled and the media and horizontal rule will be automatically
380+
* added after the currently selected block. For example after the list in which you have your cursor.
381+
*/
382+
fun addMediaAfterBlocks() {
383+
this.shouldAddMediaInline = false
384+
}
385+
372386
// Newer AppCompatEditText returns Editable?, and using that would require changing all of Aztec to not use `text.`
373387
override fun getText(): Editable {
374388
return super.getText()!!
@@ -443,6 +457,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
443457
styles.getBoolean(R.styleable.AztecText_linkUnderline, true)))
444458

445459
lineBlockFormatter = LineBlockFormatter(this)
460+
mediaFormatter = MediaFormatter(this)
446461

447462
styles.recycle()
448463

@@ -683,6 +698,9 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
683698
history.beforeTextChanged(this@AztecText)
684699
}
685700
wasStyleRemoved = blockFormatter.tryRemoveBlockStyleFromFirstLine()
701+
if (shouldAddMediaInline) {
702+
blockFormatter.moveSelectionIfImageSelected()
703+
}
686704

687705
if (selectionStart == 0 || selectionEnd == 0) {
688706
deleteInlineStyleFromTheBeginning()
@@ -1151,7 +1169,9 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
11511169
AztecTextFormat.FORMAT_ALIGN_RIGHT -> return blockFormatter.toggleTextAlignment(textFormat)
11521170
AztecTextFormat.FORMAT_PREFORMAT -> blockFormatter.togglePreformat()
11531171
AztecTextFormat.FORMAT_QUOTE -> blockFormatter.toggleQuote()
1154-
AztecTextFormat.FORMAT_HORIZONTAL_RULE -> lineBlockFormatter.applyHorizontalRule()
1172+
AztecTextFormat.FORMAT_HORIZONTAL_RULE -> {
1173+
mediaFormatter.applyHorizontalRule(shouldAddMediaInline)
1174+
}
11551175
else -> {
11561176
plugins.filter { it is IToolbarButton && it.action.textFormats.contains(textFormat) }
11571177
.map { it as IToolbarButton }
@@ -1940,11 +1960,11 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
19401960
}
19411961

19421962
fun insertImage(drawable: Drawable?, attributes: Attributes) {
1943-
lineBlockFormatter.insertImage(drawable, attributes, onImageTappedListener, onMediaDeletedListener)
1963+
mediaFormatter.insertImage(shouldAddMediaInline, drawable, attributes, onImageTappedListener, onMediaDeletedListener)
19441964
}
19451965

19461966
fun insertVideo(drawable: Drawable?, attributes: Attributes) {
1947-
lineBlockFormatter.insertVideo(drawable, attributes, onVideoTappedListener, onMediaDeletedListener)
1967+
mediaFormatter.insertVideo(shouldAddMediaInline, drawable, attributes, onVideoTappedListener, onMediaDeletedListener)
19481968
}
19491969

19501970
fun removeMedia(attributePredicate: AttributePredicate) {

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

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package org.wordpress.aztec.formatting
22

3+
import android.graphics.drawable.Drawable
34
import android.text.Editable
45
import android.text.Layout
6+
import android.text.SpannableStringBuilder
57
import android.text.Spanned
68
import android.text.TextUtils
9+
import androidx.appcompat.content.res.AppCompatResources
710
import androidx.core.text.TextDirectionHeuristicsCompat
811
import org.wordpress.android.util.AppLog
912
import org.wordpress.aztec.AlignmentRendering
@@ -12,17 +15,23 @@ import org.wordpress.aztec.AztecText
1215
import org.wordpress.aztec.AztecTextFormat
1316
import org.wordpress.aztec.Constants
1417
import org.wordpress.aztec.ITextFormat
18+
import org.wordpress.aztec.R
1519
import org.wordpress.aztec.handlers.BlockHandler
1620
import org.wordpress.aztec.handlers.HeadingHandler
1721
import org.wordpress.aztec.handlers.ListItemHandler
1822
import org.wordpress.aztec.spans.AztecHeadingSpan
23+
import org.wordpress.aztec.spans.AztecHorizontalRuleSpan
24+
import org.wordpress.aztec.spans.AztecImageSpan
1925
import org.wordpress.aztec.spans.AztecListItemSpan
2026
import org.wordpress.aztec.spans.AztecListSpan
27+
import org.wordpress.aztec.spans.AztecMediaClickableSpan
28+
import org.wordpress.aztec.spans.AztecMediaSpan
2129
import org.wordpress.aztec.spans.AztecOrderedListSpan
2230
import org.wordpress.aztec.spans.AztecPreformatSpan
2331
import org.wordpress.aztec.spans.AztecQuoteSpan
2432
import org.wordpress.aztec.spans.AztecTaskListSpan
2533
import org.wordpress.aztec.spans.AztecUnorderedListSpan
34+
import org.wordpress.aztec.spans.AztecVideoSpan
2635
import org.wordpress.aztec.spans.IAztecAlignmentSpan
2736
import org.wordpress.aztec.spans.IAztecBlockSpan
2837
import org.wordpress.aztec.spans.IAztecCompositeBlockSpan
@@ -38,6 +47,8 @@ import org.wordpress.aztec.spans.createPreformatSpan
3847
import org.wordpress.aztec.spans.createTaskListSpan
3948
import org.wordpress.aztec.spans.createUnorderedListSpan
4049
import org.wordpress.aztec.util.SpanWrapper
50+
import org.wordpress.aztec.watchers.EndOfBufferMarkerAdder
51+
import org.xml.sax.Attributes
4152
import java.util.Arrays
4253
import kotlin.reflect.KClass
4354

@@ -283,6 +294,23 @@ class BlockFormatter(editor: AztecText,
283294
}
284295
}
285296

297+
fun moveSelectionIfImageSelected() {
298+
if (selectionStart == selectionEnd &&
299+
(hasImageRightAfterSelection() || hasHorizontalRuleRightAfterSelection())) {
300+
editor.setSelection(selectionStart - 1)
301+
}
302+
}
303+
304+
private fun hasImageRightAfterSelection() =
305+
editableText.getSpans(selectionStart, selectionEnd, AztecMediaSpan::class.java).any {
306+
editableText.getSpanStart(it) == selectionStart
307+
}
308+
309+
private fun hasHorizontalRuleRightAfterSelection() =
310+
editableText.getSpans(selectionStart, selectionEnd, AztecHorizontalRuleSpan::class.java).any {
311+
editableText.getSpanStart(it) == selectionStart
312+
}
313+
286314
fun removeBlockStyle(textFormat: ITextFormat) {
287315
removeBlockStyle(textFormat, selectionStart, selectionEnd, makeBlock(textFormat, 0).map { it -> it.javaClass })
288316
}
@@ -417,7 +445,7 @@ class BlockFormatter(editor: AztecText,
417445
}
418446
}
419447

420-
fun getAlignment(textFormat: ITextFormat?, text: CharSequence) : Layout.Alignment? {
448+
fun getAlignment(textFormat: ITextFormat?, text: CharSequence): Layout.Alignment? {
421449
val direction = TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR
422450
val isRtl = direction.isRtl(text, 0, text.length)
423451

@@ -528,7 +556,7 @@ class BlockFormatter(editor: AztecText,
528556
return delimiters.distinct().sorted()
529557
}
530558

531-
private fun checkBound(bounds: HashMap<Int, Int>, key: Int, delimiters: ArrayList<Int>, lastIndex: Int) : Int {
559+
private fun checkBound(bounds: HashMap<Int, Int>, key: Int, delimiters: ArrayList<Int>, lastIndex: Int): Int {
532560
if (bounds[key]!! != bounds[lastIndex]!!) {
533561
if (bounds[key]!! < bounds[lastIndex]!!) {
534562
delimiters.add(key)
@@ -1028,7 +1056,7 @@ class BlockFormatter(editor: AztecText,
10281056
return editableText.getSpans(selStart, selEnd, IAztecAlignmentSpan::class.java)
10291057
.filter {
10301058
textFormat == null || it.align == getAlignment(textFormat,
1031-
editableText.substring(editableText.getSpanStart(it) until editableText.getSpanEnd(it)))
1059+
editableText.substring(editableText.getSpanStart(it) until editableText.getSpanEnd(it)))
10321060
}
10331061
.filter {
10341062
val spanStart = editableText.getSpanStart(it)
Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
11
package org.wordpress.aztec.formatting
22

3-
import android.graphics.drawable.Drawable
4-
import android.text.SpannableStringBuilder
5-
import android.text.Spanned
63
import android.text.TextUtils
7-
import androidx.appcompat.content.res.AppCompatResources
8-
import org.wordpress.aztec.AztecAttributes
94
import org.wordpress.aztec.AztecText
10-
import org.wordpress.aztec.AztecText.OnImageTappedListener
11-
import org.wordpress.aztec.AztecText.OnVideoTappedListener
125
import org.wordpress.aztec.AztecTextFormat
13-
import org.wordpress.aztec.Constants
146
import org.wordpress.aztec.ITextFormat
15-
import org.wordpress.aztec.R
167
import org.wordpress.aztec.spans.AztecHeadingSpan
17-
import org.wordpress.aztec.spans.AztecHorizontalRuleSpan
18-
import org.wordpress.aztec.spans.AztecImageSpan
19-
import org.wordpress.aztec.spans.AztecMediaClickableSpan
20-
import org.wordpress.aztec.spans.AztecMediaSpan
21-
import org.wordpress.aztec.spans.AztecVideoSpan
22-
import org.wordpress.aztec.spans.IAztecNestable
23-
import org.wordpress.aztec.watchers.EndOfBufferMarkerAdder
24-
import org.xml.sax.Attributes
25-
import java.util.ArrayList
268

279
class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) {
2810

@@ -94,77 +76,4 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) {
9476

9577
return false
9678
}
97-
98-
fun applyHorizontalRule() {
99-
editor.removeInlineStylesFromRange(selectionStart, selectionEnd)
100-
editor.removeBlockStylesFromRange(selectionStart, selectionEnd, true)
101-
102-
val nestingLevel = IAztecNestable.getNestingLevelAt(editableText, selectionStart)
103-
104-
val span = AztecHorizontalRuleSpan(
105-
editor.context,
106-
AppCompatResources.getDrawable(editor.context, R.drawable.img_hr)!!,
107-
nestingLevel,
108-
AztecAttributes(),
109-
editor
110-
)
111-
112-
val builder = SpannableStringBuilder(Constants.MAGIC_STRING)
113-
builder.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
114-
115-
val start = selectionStart
116-
editableText.replace(start, selectionEnd, builder)
117-
118-
val newSelectionPosition = editableText.indexOf(Constants.MAGIC_CHAR, start) + 1
119-
editor.setSelection(newSelectionPosition)
120-
}
121-
122-
fun insertVideo(drawable: Drawable?, attributes: Attributes, onVideoTappedListener: OnVideoTappedListener?,
123-
onMediaDeletedListener: AztecText.OnMediaDeletedListener?) {
124-
val nestingLevel = IAztecNestable.getNestingLevelAt(editableText, selectionStart)
125-
val span = AztecVideoSpan(editor.context, drawable, nestingLevel, AztecAttributes(attributes), onVideoTappedListener,
126-
onMediaDeletedListener, editor)
127-
insertMedia(span)
128-
}
129-
130-
fun insertImage(drawable: Drawable?, attributes: Attributes, onImageTappedListener: OnImageTappedListener?,
131-
onMediaDeletedListener: AztecText.OnMediaDeletedListener?) {
132-
val nestingLevel = IAztecNestable.getNestingLevelAt(editableText, selectionStart)
133-
val span = AztecImageSpan(editor.context, drawable, nestingLevel, AztecAttributes(attributes), onImageTappedListener,
134-
onMediaDeletedListener, editor)
135-
insertMedia(span)
136-
}
137-
138-
private fun insertMedia(span: AztecMediaSpan) {
139-
editor.removeInlineStylesFromRange(selectionStart, selectionEnd)
140-
141-
val ssb = SpannableStringBuilder(Constants.IMG_STRING)
142-
143-
ssb.setSpan(
144-
span,
145-
0,
146-
1,
147-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
148-
)
149-
150-
ssb.setSpan(
151-
AztecMediaClickableSpan(span),
152-
0,
153-
1,
154-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
155-
)
156-
157-
// We need to be sure the cursor is placed correctly after media insertion
158-
// Note that media has '\n' around them when needed
159-
val isLastItem = selectionEnd == EndOfBufferMarkerAdder.safeLength(editor)
160-
editableText.replace(selectionStart, selectionEnd, ssb)
161-
162-
val newSelection = if (isLastItem) {
163-
EndOfBufferMarkerAdder.safeLength(editor)
164-
} else {
165-
if (selectionEnd < EndOfBufferMarkerAdder.safeLength(editor)) selectionEnd + 1 else selectionEnd
166-
}
167-
editor.setSelection(newSelection)
168-
editor.isMediaAdded = true
169-
}
17079
}

0 commit comments

Comments
 (0)