@openeditor/react-native-prose-editor 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -9
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +23 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +2 -2
- package/android/src/main/java/com/apollohg/editor/NativeBlockEditorSurface.kt +381 -49
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +110 -169
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +1 -1
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +124 -337
- package/dist/EditorTheme.d.ts +8 -0
- package/ios/NativeBlockEditorSurface.swift +323 -61
- package/ios/NativeEditorExpoView.swift +114 -117
- package/ios/NativeEditorModule.swift +1 -1
- package/ios/NativeInputSupport.swift +0 -302
- package/ios/PositionBridge.swift +22 -548
- package/ios/RichTextEditorView.swift +229 -4664
- package/package.json +1 -1
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +0 -50
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +0 -4068
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +0 -1009
- package/android/src/main/java/com/apollohg/editor/InputSnapshotSupport.kt +0 -6
|
@@ -449,7 +449,7 @@ class NativeEditorModule : Module() {
|
|
|
449
449
|
view.setEditable(editable)
|
|
450
450
|
}
|
|
451
451
|
Prop("placeholder") { view: NativeEditorExpoView, placeholder: String ->
|
|
452
|
-
view.richTextView.
|
|
452
|
+
view.richTextView.blockSurface.placeholder = placeholder
|
|
453
453
|
}
|
|
454
454
|
Prop("autoFocus") { view: NativeEditorExpoView, autoFocus: Boolean ->
|
|
455
455
|
view.setAutoFocus(autoFocus)
|
|
@@ -5,159 +5,62 @@ import android.graphics.Color
|
|
|
5
5
|
import android.graphics.RectF
|
|
6
6
|
import android.graphics.drawable.GradientDrawable
|
|
7
7
|
import android.util.AttributeSet
|
|
8
|
-
import android.view.MotionEvent
|
|
9
8
|
import android.view.ViewGroup
|
|
10
|
-
import android.view.inputmethod.InputMethodManager
|
|
11
9
|
import android.widget.FrameLayout
|
|
12
10
|
import android.widget.LinearLayout
|
|
13
|
-
import android.widget.ScrollView
|
|
14
11
|
import uniffi.editor_core.*
|
|
15
12
|
|
|
16
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The single Android editor host. Its only text inputs are the editable leaves
|
|
15
|
+
* owned by [NativeBlockEditorSurface]; no whole-document EditText exists.
|
|
16
|
+
*/
|
|
17
17
|
class RichTextEditorView @JvmOverloads constructor(
|
|
18
18
|
context: Context,
|
|
19
19
|
attrs: AttributeSet? = null,
|
|
20
20
|
defStyleAttr: Int = 0
|
|
21
21
|
) : LinearLayout(context, attrs, defStyleAttr) {
|
|
22
|
-
val editorViewport
|
|
22
|
+
val editorViewport = FrameLayout(context)
|
|
23
|
+
val blockSurface = NativeBlockEditorSurface(context)
|
|
24
|
+
private val remoteSelectionOverlayView = RemoteSelectionOverlayView(context)
|
|
25
|
+
private val imageResizeOverlayView = ImageResizeOverlayView(context)
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
val canScroll = canScrollVertically(-1) || canScrollVertically(1)
|
|
27
|
-
if (!canScroll) return
|
|
28
|
-
when (action) {
|
|
29
|
-
MotionEvent.ACTION_DOWN,
|
|
30
|
-
MotionEvent.ACTION_MOVE -> parent?.requestDisallowInterceptTouchEvent(true)
|
|
31
|
-
MotionEvent.ACTION_UP,
|
|
32
|
-
MotionEvent.ACTION_CANCEL -> parent?.requestDisallowInterceptTouchEvent(false)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
|
37
|
-
updateParentIntercept(ev.actionMasked)
|
|
38
|
-
return super.onInterceptTouchEvent(ev)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
42
|
-
updateParentIntercept(ev.actionMasked)
|
|
43
|
-
return super.onTouchEvent(ev)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
val editorEditText: EditorEditText
|
|
48
|
-
val editorScrollView: EditorScrollView
|
|
49
|
-
val blockSurface: NativeBlockEditorSurface
|
|
50
|
-
private val remoteSelectionOverlayView: RemoteSelectionOverlayView
|
|
51
|
-
private val imageResizeOverlayView: ImageResizeOverlayView
|
|
52
|
-
|
|
53
|
-
private var heightBehavior: EditorHeightBehavior = EditorHeightBehavior.FIXED
|
|
27
|
+
private var currentEditorId = 0L
|
|
28
|
+
private var heightBehavior = EditorHeightBehavior.FIXED
|
|
54
29
|
private var imageResizingEnabled = true
|
|
30
|
+
private var viewportBottomInsetPx = 0
|
|
31
|
+
private var deferEditorUnbindOnDetach = false
|
|
55
32
|
private var theme: EditorTheme? = null
|
|
56
|
-
private var baseBackgroundColor
|
|
57
|
-
private var viewportBottomInsetPx: Int = 0
|
|
58
|
-
internal var appliedCornerRadiusPx: Float = 0f
|
|
59
|
-
internal var appliedBackgroundColorForTesting: Int = Color.WHITE
|
|
33
|
+
private var baseBackgroundColor = Color.WHITE
|
|
60
34
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
private var isSyncingBlockSurfaceUpdateToInputAdapter = false
|
|
35
|
+
internal var appliedCornerRadiusPx = 0f
|
|
36
|
+
internal var appliedBackgroundColorForTesting = Color.WHITE
|
|
64
37
|
internal var onBeforeDetachedFromWindow: (() -> Unit)? = null
|
|
38
|
+
var onSelectionChange: ((Int, Int) -> Unit)? = null
|
|
39
|
+
var onEditorUpdate: ((String) -> Unit)? = null
|
|
40
|
+
var onFocusChange: ((Boolean) -> Unit)? = null
|
|
65
41
|
|
|
66
|
-
/** Binds or unbinds the Rust editor instance. */
|
|
67
42
|
var editorId: Long
|
|
68
43
|
get() = currentEditorId
|
|
69
|
-
set(value)
|
|
70
|
-
|
|
71
|
-
|
|
44
|
+
set(value) = bindEditor(value)
|
|
45
|
+
|
|
46
|
+
val activeEditText get() = blockSurface.activeEditText
|
|
47
|
+
val isEditorFocused get() = blockSurface.isEditorFocused
|
|
48
|
+
val editorIsEditable get() = blockSurface.isEditable
|
|
49
|
+
val editorTheme get() = theme
|
|
72
50
|
|
|
73
51
|
init {
|
|
74
52
|
orientation = VERTICAL
|
|
53
|
+
editorViewport.addView(blockSurface, matchParentParams())
|
|
54
|
+
editorViewport.addView(remoteSelectionOverlayView, matchParentParams())
|
|
55
|
+
editorViewport.addView(imageResizeOverlayView, matchParentParams())
|
|
56
|
+
addView(editorViewport, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
75
57
|
|
|
76
|
-
editorEditText = EditorEditText(context)
|
|
77
|
-
editorScrollView = EditorScrollView(context).apply {
|
|
78
|
-
clipToPadding = false
|
|
79
|
-
isFillViewport = false
|
|
80
|
-
alpha = 0.01f
|
|
81
|
-
}
|
|
82
|
-
blockSurface = NativeBlockEditorSurface(context)
|
|
83
|
-
editorViewport = FrameLayout(context)
|
|
84
|
-
remoteSelectionOverlayView = RemoteSelectionOverlayView(context)
|
|
85
|
-
imageResizeOverlayView = ImageResizeOverlayView(context)
|
|
86
|
-
editorScrollView.addView(editorEditText, createEditorLayoutParams())
|
|
87
|
-
editorViewport.addView(
|
|
88
|
-
editorScrollView,
|
|
89
|
-
FrameLayout.LayoutParams(
|
|
90
|
-
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
91
|
-
ViewGroup.LayoutParams.MATCH_PARENT
|
|
92
|
-
)
|
|
93
|
-
)
|
|
94
|
-
editorViewport.addView(
|
|
95
|
-
blockSurface,
|
|
96
|
-
FrameLayout.LayoutParams(
|
|
97
|
-
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
98
|
-
ViewGroup.LayoutParams.MATCH_PARENT
|
|
99
|
-
)
|
|
100
|
-
)
|
|
101
|
-
editorViewport.addView(
|
|
102
|
-
remoteSelectionOverlayView,
|
|
103
|
-
FrameLayout.LayoutParams(
|
|
104
|
-
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
105
|
-
ViewGroup.LayoutParams.MATCH_PARENT
|
|
106
|
-
)
|
|
107
|
-
)
|
|
108
|
-
editorViewport.addView(
|
|
109
|
-
imageResizeOverlayView,
|
|
110
|
-
FrameLayout.LayoutParams(
|
|
111
|
-
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
112
|
-
ViewGroup.LayoutParams.MATCH_PARENT
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
58
|
remoteSelectionOverlayView.bind(this)
|
|
116
59
|
imageResizeOverlayView.bind(this)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (editorId != 0L && !isSyncingBlockSurfaceUpdateToInputAdapter) {
|
|
122
|
-
blockSurface.applyUpdateJSON(
|
|
123
|
-
editorGetSelectionState(editorId.toULong()),
|
|
124
|
-
editorId
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
refreshOverlays()
|
|
128
|
-
}
|
|
129
|
-
editorEditText.onUpdateJSONApplied = appliedUpdate@ { updateJSON ->
|
|
130
|
-
if (isSyncingBlockSurfaceUpdateToInputAdapter) return@appliedUpdate
|
|
131
|
-
blockSurface.applyUpdateJSON(updateJSON, editorId)
|
|
132
|
-
}
|
|
133
|
-
blockSurface.onAppliedUpdate = { updateJSON ->
|
|
134
|
-
isSyncingBlockSurfaceUpdateToInputAdapter = true
|
|
135
|
-
try {
|
|
136
|
-
editorEditText.applyUpdateJSON(updateJSON, notifyListener = true)
|
|
137
|
-
} finally {
|
|
138
|
-
isSyncingBlockSurfaceUpdateToInputAdapter = false
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
blockSurface.onRequestTextInput = requestTextInput@ { docPos ->
|
|
142
|
-
if (editorId == 0L || !editorEditText.isEditable) return@requestTextInput
|
|
143
|
-
editorSetSelection(editorId.toULong(), docPos.toUInt(), docPos.toUInt())
|
|
144
|
-
isSyncingBlockSurfaceUpdateToInputAdapter = true
|
|
145
|
-
try {
|
|
146
|
-
editorEditText.applyUpdateJSON(
|
|
147
|
-
editorGetSelectionState(editorId.toULong()),
|
|
148
|
-
notifyListener = true
|
|
149
|
-
)
|
|
150
|
-
} finally {
|
|
151
|
-
isSyncingBlockSurfaceUpdateToInputAdapter = false
|
|
152
|
-
}
|
|
153
|
-
editorEditText.requestFocus()
|
|
154
|
-
context.getSystemService(InputMethodManager::class.java)
|
|
155
|
-
?.showSoftInput(editorEditText, InputMethodManager.SHOW_IMPLICIT)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
addView(editorViewport, createContainerLayoutParams())
|
|
159
|
-
updateScrollContainerAppearance()
|
|
160
|
-
updateScrollContainerInsets()
|
|
60
|
+
blockSurface.onSelectionChange = { anchor, head -> onSelectionChange?.invoke(anchor, head) }
|
|
61
|
+
blockSurface.onEditorUpdate = { update -> onEditorUpdate?.invoke(update) }
|
|
62
|
+
blockSurface.onFocusChange = { focused -> onFocusChange?.invoke(focused) }
|
|
63
|
+
updateAppearance()
|
|
161
64
|
}
|
|
162
65
|
|
|
163
66
|
fun configure(
|
|
@@ -166,257 +69,134 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
166
69
|
backgroundColor: Int = Color.WHITE
|
|
167
70
|
) {
|
|
168
71
|
baseBackgroundColor = backgroundColor
|
|
169
|
-
editorEditText.setBaseStyle(textSizePx, textColor, backgroundColor)
|
|
170
72
|
blockSurface.configure(textSizePx, textColor, backgroundColor)
|
|
171
|
-
|
|
172
|
-
refreshOverlays()
|
|
73
|
+
updateAppearance()
|
|
173
74
|
}
|
|
174
75
|
|
|
175
76
|
fun applyTheme(theme: EditorTheme?) {
|
|
176
77
|
this.theme = theme
|
|
177
|
-
val previousScrollY = editorScrollView.scrollY
|
|
178
|
-
editorEditText.applyTheme(theme)
|
|
179
78
|
blockSurface.applyTheme(theme)
|
|
180
|
-
|
|
181
|
-
updateScrollContainerInsets()
|
|
182
|
-
if (heightBehavior == EditorHeightBehavior.FIXED) {
|
|
183
|
-
editorScrollView.post {
|
|
184
|
-
val childHeight = editorScrollView.getChildAt(0)?.height ?: 0
|
|
185
|
-
val maxScrollY = maxOf(
|
|
186
|
-
0,
|
|
187
|
-
childHeight + editorScrollView.paddingTop + editorScrollView.paddingBottom - editorScrollView.height
|
|
188
|
-
)
|
|
189
|
-
editorScrollView.scrollTo(0, previousScrollY.coerceIn(0, maxScrollY))
|
|
190
|
-
refreshOverlays()
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
refreshOverlays()
|
|
79
|
+
updateAppearance()
|
|
194
80
|
}
|
|
195
81
|
|
|
196
82
|
fun setEditable(editable: Boolean) {
|
|
197
|
-
editorEditText.isEditable = editable
|
|
198
83
|
blockSurface.isEditable = editable
|
|
199
|
-
if (!editable)
|
|
84
|
+
if (!editable) blurEditor()
|
|
200
85
|
}
|
|
201
86
|
|
|
202
|
-
fun setHeightBehavior(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
editorViewport.layoutParams = createContainerLayoutParams()
|
|
208
|
-
editorScrollView.isVerticalScrollBarEnabled = heightBehavior == EditorHeightBehavior.FIXED
|
|
209
|
-
editorScrollView.overScrollMode = if (heightBehavior == EditorHeightBehavior.FIXED) {
|
|
210
|
-
OVER_SCROLL_IF_CONTENT_SCROLLS
|
|
211
|
-
} else {
|
|
212
|
-
OVER_SCROLL_NEVER
|
|
213
|
-
}
|
|
214
|
-
blockSurface.isScrollingEnabled = heightBehavior == EditorHeightBehavior.FIXED
|
|
215
|
-
blockSurface.isVerticalScrollBarEnabled = heightBehavior == EditorHeightBehavior.FIXED
|
|
216
|
-
blockSurface.overScrollMode = if (heightBehavior == EditorHeightBehavior.FIXED) {
|
|
217
|
-
OVER_SCROLL_IF_CONTENT_SCROLLS
|
|
218
|
-
} else {
|
|
219
|
-
OVER_SCROLL_NEVER
|
|
220
|
-
}
|
|
221
|
-
updateScrollContainerInsets()
|
|
222
|
-
refreshOverlays()
|
|
87
|
+
fun setHeightBehavior(value: EditorHeightBehavior) {
|
|
88
|
+
heightBehavior = value
|
|
89
|
+
blockSurface.isScrollingEnabled = value == EditorHeightBehavior.FIXED
|
|
90
|
+
blockSurface.isVerticalScrollBarEnabled = value == EditorHeightBehavior.FIXED
|
|
91
|
+
layoutParams = createContainerLayoutParams()
|
|
223
92
|
requestLayout()
|
|
224
93
|
}
|
|
225
94
|
|
|
226
95
|
fun setImageResizingEnabled(enabled: Boolean) {
|
|
227
|
-
if (imageResizingEnabled == enabled) return
|
|
228
96
|
imageResizingEnabled = enabled
|
|
229
|
-
|
|
230
|
-
refreshOverlays()
|
|
97
|
+
imageResizeOverlayView.refresh()
|
|
231
98
|
}
|
|
232
99
|
|
|
233
|
-
fun setViewportBottomInsetPx(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
100
|
+
fun setViewportBottomInsetPx(value: Int) {
|
|
101
|
+
viewportBottomInsetPx = value.coerceAtLeast(0)
|
|
102
|
+
blockSurface.setPadding(
|
|
103
|
+
blockSurface.paddingLeft,
|
|
104
|
+
blockSurface.paddingTop,
|
|
105
|
+
blockSurface.paddingRight,
|
|
106
|
+
viewportBottomInsetPx
|
|
107
|
+
)
|
|
240
108
|
requestLayout()
|
|
241
109
|
}
|
|
242
110
|
|
|
243
|
-
internal fun viewportBottomInsetPxForTesting()
|
|
244
|
-
|
|
245
|
-
fun setRemoteSelections(selections: List<RemoteSelectionDecoration>) {
|
|
111
|
+
internal fun viewportBottomInsetPxForTesting() = viewportBottomInsetPx
|
|
112
|
+
fun setRemoteSelections(selections: List<RemoteSelectionDecoration>) =
|
|
246
113
|
remoteSelectionOverlayView.setRemoteSelections(selections)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
fun refreshRemoteSelections() {
|
|
250
|
-
if (!remoteSelectionOverlayView.hasSelectionsOrCachedGeometry()) return
|
|
251
|
-
remoteSelectionOverlayView.refreshGeometry()
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
fun imageResizeOverlayRectForTesting(): android.graphics.RectF? =
|
|
255
|
-
imageResizeOverlayView.visibleRectForTesting()
|
|
256
|
-
|
|
257
|
-
fun resizeSelectedImageForTesting(widthPx: Float, heightPx: Float) {
|
|
258
|
-
imageResizeOverlayView.simulateResizeForTesting(widthPx, heightPx)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
fun remoteSelectionDebugSnapshotsForTesting(): List<RemoteSelectionDebugSnapshot> =
|
|
262
|
-
remoteSelectionOverlayView.debugSnapshotsForTesting()
|
|
263
|
-
|
|
114
|
+
fun refreshRemoteSelections() = remoteSelectionOverlayView.refreshGeometry()
|
|
115
|
+
fun remoteSelectionDebugSnapshotsForTesting() = remoteSelectionOverlayView.debugSnapshotsForTesting()
|
|
264
116
|
fun setRemoteSelectionEditorIdForTesting(editorId: Long) {
|
|
265
117
|
remoteSelectionOverlayView.editorIdOverrideForTesting = editorId
|
|
266
118
|
}
|
|
119
|
+
fun imageResizeOverlayRectForTesting() = imageResizeOverlayView.visibleRectForTesting()
|
|
120
|
+
fun resizeSelectedImageForTesting(widthPx: Float, heightPx: Float) =
|
|
121
|
+
imageResizeOverlayView.simulateResizeForTesting(widthPx, heightPx)
|
|
267
122
|
|
|
268
123
|
fun setContent(html: String) {
|
|
269
124
|
if (editorId == 0L) return
|
|
270
|
-
editorSetHtml(editorId.toULong(), html)
|
|
271
|
-
applyFullStateToInputAndSurface(editorGetCurrentState(editorId.toULong()))
|
|
125
|
+
applyEditorUpdate(editorSetHtml(editorId.toULong(), html))
|
|
272
126
|
}
|
|
273
127
|
|
|
274
128
|
fun setContent(json: org.json.JSONObject) {
|
|
275
129
|
if (editorId == 0L) return
|
|
276
|
-
editorSetJson(editorId.toULong(), json.toString())
|
|
277
|
-
applyFullStateToInputAndSurface(editorGetCurrentState(editorId.toULong()))
|
|
130
|
+
applyEditorUpdate(editorSetJson(editorId.toULong(), json.toString()))
|
|
278
131
|
}
|
|
279
132
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
isSyncingBlockSurfaceUpdateToInputAdapter = false
|
|
290
|
-
}
|
|
291
|
-
blockSurface.applyUpdateJSON(stateJSON, editorId)
|
|
292
|
-
}
|
|
133
|
+
fun focusEditor() = blockSurface.focus()
|
|
134
|
+
fun blurEditor() = blockSurface.blur()
|
|
135
|
+
fun hasActiveComposition() = blockSurface.hasActiveComposition()
|
|
136
|
+
fun currentScalarSelection() = blockSurface.currentScalarSelection()
|
|
137
|
+
fun activeDocPositionForUtf16Offset(offset: Int) = blockSurface.activeDocPositionForUtf16Offset(offset)
|
|
138
|
+
fun applyEditorUpdate(updateJSON: String) = blockSurface.applyUpdateJSON(updateJSON, editorId)
|
|
139
|
+
fun setAutoCapitalize(value: String?) = blockSurface.setAutoCapitalize(value)
|
|
140
|
+
fun setAutoCorrect(value: Boolean?) = blockSurface.setAutoCorrect(value)
|
|
141
|
+
fun setKeyboardType(value: String?) = blockSurface.setKeyboardType(value)
|
|
293
142
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
internal fun setEditorIdWhileDetached(value: Long) {
|
|
301
|
-
setEditorId(value, bindEditor = false)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
internal fun deferEditorUnbindOnNextDetach() {
|
|
305
|
-
deferEditorUnbindOnDetach = true
|
|
143
|
+
fun performEditorCommand(command: () -> String): String {
|
|
144
|
+
val update = command()
|
|
145
|
+
applyEditorUpdate(update)
|
|
146
|
+
onEditorUpdate?.invoke(update)
|
|
147
|
+
return update
|
|
306
148
|
}
|
|
307
149
|
|
|
308
|
-
internal fun
|
|
309
|
-
|
|
150
|
+
internal fun rebindEditorIfNeeded(notifyListener: Boolean = true) {
|
|
151
|
+
if (editorId != 0L) applyEditorUpdate(editorGetCurrentState(editorId.toULong()))
|
|
310
152
|
}
|
|
311
153
|
|
|
154
|
+
internal fun setEditorIdWhileDetached(value: Long) = bindEditor(value)
|
|
155
|
+
internal fun deferEditorUnbindOnNextDetach() { deferEditorUnbindOnDetach = true }
|
|
156
|
+
internal fun clearDeferredEditorUnbind() { deferEditorUnbindOnDetach = false }
|
|
312
157
|
internal fun unbindEditorForDetachedViewIfNeeded() {
|
|
313
|
-
if (
|
|
158
|
+
if (!deferEditorUnbindOnDetach) blockSurface.bind(0)
|
|
314
159
|
deferEditorUnbindOnDetach = false
|
|
315
|
-
if (editorId != 0L) {
|
|
316
|
-
editorEditText.unbindEditor()
|
|
317
|
-
}
|
|
318
160
|
}
|
|
319
161
|
|
|
320
|
-
private fun
|
|
321
|
-
|
|
322
|
-
if (currentEditorId == value && editorEditText.editorId == targetBoundEditorId) return
|
|
323
|
-
if (currentEditorId != value || editorEditText.editorId != targetBoundEditorId) {
|
|
324
|
-
editorEditText.discardTransientNativeInputForEditorRebind()
|
|
325
|
-
}
|
|
162
|
+
private fun bindEditor(value: Long) {
|
|
163
|
+
if (currentEditorId == value) return
|
|
326
164
|
currentEditorId = value
|
|
327
165
|
blockSurface.bind(value)
|
|
328
|
-
if (
|
|
329
|
-
editorEditText.bindEditor(value, notifyListener = notifyListener)
|
|
330
|
-
} else {
|
|
331
|
-
editorEditText.unbindEditor()
|
|
332
|
-
}
|
|
333
|
-
refreshOverlays()
|
|
166
|
+
if (value != 0L) applyEditorUpdate(editorGetCurrentState(value.toULong()))
|
|
334
167
|
}
|
|
335
168
|
|
|
336
169
|
override fun onAttachedToWindow() {
|
|
337
170
|
super.onAttachedToWindow()
|
|
338
|
-
|
|
339
|
-
rebindEditorIfNeeded()
|
|
171
|
+
rebindEditorIfNeeded(false)
|
|
340
172
|
}
|
|
341
173
|
|
|
342
174
|
override fun onDetachedFromWindow() {
|
|
343
175
|
onBeforeDetachedFromWindow?.invoke()
|
|
176
|
+
unbindEditorForDetachedViewIfNeeded()
|
|
344
177
|
super.onDetachedFromWindow()
|
|
345
|
-
if (editorId != 0L && !deferEditorUnbindOnDetach) {
|
|
346
|
-
editorEditText.unbindEditor()
|
|
347
|
-
}
|
|
348
178
|
}
|
|
349
179
|
|
|
350
180
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
351
|
-
if (heightBehavior != EditorHeightBehavior.AUTO_GROW) {
|
|
352
|
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
353
|
-
return
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
val childWidthSpec = getChildMeasureSpec(
|
|
357
|
-
widthMeasureSpec,
|
|
358
|
-
paddingLeft + paddingRight,
|
|
359
|
-
editorViewport.layoutParams.width
|
|
360
|
-
)
|
|
361
|
-
val childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
|
362
|
-
editorViewport.measure(childWidthSpec, childHeightSpec)
|
|
363
|
-
|
|
364
|
-
val measuredWidth = resolveSize(
|
|
365
|
-
editorViewport.measuredWidth + paddingLeft + paddingRight,
|
|
366
|
-
widthMeasureSpec
|
|
367
|
-
)
|
|
368
|
-
val desiredHeight = editorViewport.measuredHeight + paddingTop + paddingBottom
|
|
369
|
-
val measuredHeight = when (MeasureSpec.getMode(heightMeasureSpec)) {
|
|
370
|
-
MeasureSpec.AT_MOST -> desiredHeight.coerceAtMost(MeasureSpec.getSize(heightMeasureSpec))
|
|
371
|
-
else -> desiredHeight
|
|
372
|
-
}
|
|
373
|
-
setMeasuredDimension(measuredWidth, measuredHeight)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
private fun updateScrollContainerAppearance() {
|
|
377
|
-
val cornerRadiusPx = (theme?.borderRadius ?: 0f) * resources.displayMetrics.density
|
|
378
|
-
val backgroundColor = theme?.backgroundColor ?: baseBackgroundColor
|
|
379
|
-
editorViewport.background = GradientDrawable().apply {
|
|
380
|
-
cornerRadius = cornerRadiusPx
|
|
381
|
-
setColor(backgroundColor)
|
|
382
|
-
}
|
|
383
|
-
editorViewport.clipToOutline = cornerRadiusPx > 0f
|
|
384
|
-
editorScrollView.setBackgroundColor(Color.TRANSPARENT)
|
|
385
|
-
appliedCornerRadiusPx = cornerRadiusPx
|
|
386
|
-
appliedBackgroundColorForTesting = backgroundColor
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private fun updateScrollContainerInsets() {
|
|
390
|
-
if (heightBehavior != EditorHeightBehavior.FIXED) {
|
|
391
|
-
editorScrollView.setPadding(0, 0, 0, 0)
|
|
392
|
-
return
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
val density = resources.displayMetrics.density
|
|
396
|
-
val topInset = ((theme?.contentInsets?.top ?: 0f) * density).toInt()
|
|
397
|
-
val bottomInset = ((theme?.contentInsets?.bottom ?: 0f) * density).toInt()
|
|
398
|
-
editorScrollView.setPadding(0, topInset, 0, bottomInset + viewportBottomInsetPx)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
private fun createContainerLayoutParams(): LayoutParams =
|
|
402
181
|
if (heightBehavior == EditorHeightBehavior.AUTO_GROW) {
|
|
403
|
-
|
|
182
|
+
val childWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, LayoutParams.MATCH_PARENT)
|
|
183
|
+
val childHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
|
184
|
+
editorViewport.measure(childWidth, childHeight)
|
|
185
|
+
setMeasuredDimension(
|
|
186
|
+
resolveSize(editorViewport.measuredWidth + paddingLeft + paddingRight, widthMeasureSpec),
|
|
187
|
+
editorViewport.measuredHeight + paddingTop + paddingBottom
|
|
188
|
+
)
|
|
404
189
|
} else {
|
|
405
|
-
|
|
190
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
406
191
|
}
|
|
192
|
+
}
|
|
407
193
|
|
|
408
|
-
|
|
409
|
-
FrameLayout.LayoutParams(
|
|
410
|
-
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
411
|
-
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
internal fun selectedImageGeometry(): EditorEditText.SelectedImageGeometry? {
|
|
194
|
+
internal fun selectedImageGeometry(): SelectedImageGeometry? {
|
|
415
195
|
if (!imageResizingEnabled) return null
|
|
416
196
|
val geometry = blockSurface.selectedImageGeometry() ?: return null
|
|
417
|
-
return
|
|
418
|
-
|
|
419
|
-
|
|
197
|
+
return SelectedImageGeometry(
|
|
198
|
+
geometry.docPos,
|
|
199
|
+
RectF(
|
|
420
200
|
editorViewport.left + blockSurface.left + geometry.rect.left,
|
|
421
201
|
editorViewport.top + blockSurface.top + geometry.rect.top,
|
|
422
202
|
editorViewport.left + blockSurface.left + geometry.rect.right,
|
|
@@ -426,7 +206,6 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
426
206
|
}
|
|
427
207
|
|
|
428
208
|
internal fun caretRect(): RectF? {
|
|
429
|
-
blockSurface.isInputFocused = editorEditText.hasFocus()
|
|
430
209
|
val rect = blockSurface.selectedCaretRect() ?: return null
|
|
431
210
|
return RectF(
|
|
432
211
|
editorViewport.left + blockSurface.left + rect.left,
|
|
@@ -436,29 +215,37 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
436
215
|
)
|
|
437
216
|
}
|
|
438
217
|
|
|
439
|
-
internal fun maximumImageWidthPx()
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
widthPx: Float,
|
|
445
|
-
heightPx: Float,
|
|
446
|
-
maximumWidthPx: Float = maximumImageWidthPx()
|
|
447
|
-
): Pair<Float, Float> {
|
|
448
|
-
val aspectRatio = maxOf(widthPx / maxOf(heightPx, 1f), 0.1f)
|
|
449
|
-
val clampedWidth = minOf(maxOf(48f, maximumWidthPx), maxOf(48f, widthPx))
|
|
450
|
-
val clampedHeight = maxOf(48f, clampedWidth / aspectRatio)
|
|
451
|
-
return clampedWidth to clampedHeight
|
|
218
|
+
internal fun maximumImageWidthPx() = blockSurface.maximumImageWidthPx()
|
|
219
|
+
internal fun clampImageSize(widthPx: Float, heightPx: Float, maximumWidthPx: Float = maximumImageWidthPx()): Pair<Float, Float> {
|
|
220
|
+
val ratio = maxOf(widthPx / maxOf(heightPx, 1f), 0.1f)
|
|
221
|
+
val width = minOf(maxOf(48f, maximumWidthPx), maxOf(48f, widthPx))
|
|
222
|
+
return width to maxOf(48f, width / ratio)
|
|
452
223
|
}
|
|
453
|
-
|
|
454
224
|
internal fun resizeImage(docPos: Int, widthPx: Float, heightPx: Float) {
|
|
455
|
-
val
|
|
456
|
-
blockSurface.resizeImage(docPos,
|
|
225
|
+
val size = clampImageSize(widthPx, heightPx)
|
|
226
|
+
blockSurface.resizeImage(docPos, size.first, size.second)
|
|
457
227
|
}
|
|
458
228
|
|
|
459
|
-
private fun
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
229
|
+
private fun updateAppearance() {
|
|
230
|
+
val backgroundColor = theme?.backgroundColor ?: baseBackgroundColor
|
|
231
|
+
val radius = (theme?.borderRadius ?: 0f) * resources.displayMetrics.density
|
|
232
|
+
appliedCornerRadiusPx = radius
|
|
233
|
+
appliedBackgroundColorForTesting = backgroundColor
|
|
234
|
+
background = GradientDrawable().apply {
|
|
235
|
+
shape = GradientDrawable.RECTANGLE
|
|
236
|
+
setColor(backgroundColor)
|
|
237
|
+
cornerRadius = radius
|
|
238
|
+
}
|
|
239
|
+
clipToOutline = radius > 0f
|
|
463
240
|
}
|
|
241
|
+
|
|
242
|
+
private fun createContainerLayoutParams() = LayoutParams(
|
|
243
|
+
LayoutParams.MATCH_PARENT,
|
|
244
|
+
if (heightBehavior == EditorHeightBehavior.AUTO_GROW) LayoutParams.WRAP_CONTENT else LayoutParams.MATCH_PARENT
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
private fun matchParentParams() = FrameLayout.LayoutParams(
|
|
248
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
249
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
250
|
+
)
|
|
464
251
|
}
|
package/dist/EditorTheme.d.ts
CHANGED
|
@@ -69,6 +69,13 @@ export interface EditorCodeBlockTheme {
|
|
|
69
69
|
paddingHorizontal?: number;
|
|
70
70
|
paddingVertical?: number;
|
|
71
71
|
}
|
|
72
|
+
export interface EditorTableTheme {
|
|
73
|
+
cellBackgroundColor?: string;
|
|
74
|
+
headerBackgroundColor?: string;
|
|
75
|
+
borderColor?: string;
|
|
76
|
+
selectionBackgroundColor?: string;
|
|
77
|
+
selectionBorderColor?: string;
|
|
78
|
+
}
|
|
72
79
|
export type EditorToolbarAppearance = 'custom' | 'native';
|
|
73
80
|
export interface EditorToolbarTheme {
|
|
74
81
|
appearance?: EditorToolbarAppearance;
|
|
@@ -99,6 +106,7 @@ export interface EditorTheme {
|
|
|
99
106
|
paragraph?: EditorTextStyle;
|
|
100
107
|
blockquote?: EditorBlockquoteTheme;
|
|
101
108
|
codeBlock?: EditorCodeBlockTheme;
|
|
109
|
+
table?: EditorTableTheme;
|
|
102
110
|
headings?: EditorHeadingTheme;
|
|
103
111
|
list?: EditorListTheme;
|
|
104
112
|
horizontalRule?: EditorHorizontalRuleTheme;
|