@svgedit/svgcanvas 7.2.6 → 7.4.1
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/CHANGES.md +6 -0
- package/common/browser.js +104 -37
- package/common/logger.js +151 -0
- package/common/util.js +96 -155
- package/core/blur-event.js +106 -42
- package/core/clear.js +13 -3
- package/core/coords.js +214 -90
- package/core/copy-elem.js +27 -13
- package/core/dataStorage.js +84 -21
- package/core/draw.js +80 -40
- package/core/elem-get-set.js +161 -77
- package/core/event.js +143 -28
- package/core/history.js +51 -31
- package/core/historyrecording.js +4 -2
- package/core/json.js +54 -12
- package/core/layer.js +11 -17
- package/core/math.js +102 -23
- package/core/namespaces.js +5 -5
- package/core/paint.js +100 -23
- package/core/paste-elem.js +58 -19
- package/core/path-actions.js +812 -791
- package/core/path-method.js +236 -37
- package/core/path.js +45 -10
- package/core/recalculate.js +438 -24
- package/core/sanitize.js +71 -34
- package/core/select.js +44 -20
- package/core/selected-elem.js +146 -31
- package/core/selection.js +16 -6
- package/core/svg-exec.js +103 -29
- package/core/svgroot.js +1 -1
- package/core/text-actions.js +327 -306
- package/core/undo.js +20 -5
- package/core/units.js +8 -6
- package/core/utilities.js +316 -203
- package/dist/svgcanvas.js +31616 -53281
- package/dist/svgcanvas.js.map +1 -1
- package/package.json +55 -54
- package/publish.md +1 -6
- package/svgcanvas.d.ts +225 -0
- package/svgcanvas.js +9 -9
- package/vite.config.mjs +20 -0
- package/rollup.config.mjs +0 -38
package/core/text-actions.js
CHANGED
|
@@ -28,68 +28,69 @@ export const init = canvas => {
|
|
|
28
28
|
/**
|
|
29
29
|
* Group: Text edit functions
|
|
30
30
|
* Functions relating to editing text elements.
|
|
31
|
-
* @
|
|
31
|
+
* @class TextActions
|
|
32
32
|
* @memberof module:svgcanvas.SvgCanvas#
|
|
33
33
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
class TextActions {
|
|
35
|
+
#curtext = null
|
|
36
|
+
#textinput = null
|
|
37
|
+
#cursor = null
|
|
38
|
+
#selblock = null
|
|
39
|
+
#blinker = null
|
|
40
|
+
#chardata = []
|
|
41
|
+
#textbb = null // , transbb;
|
|
42
|
+
#matrix = null
|
|
43
|
+
#lastX = null
|
|
44
|
+
#lastY = null
|
|
45
|
+
#allowDbl = false
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
*
|
|
49
49
|
* @param {Integer} index
|
|
50
50
|
* @returns {void}
|
|
51
|
+
* @private
|
|
51
52
|
*/
|
|
52
|
-
|
|
53
|
-
const empty = textinput.value === ''
|
|
54
|
-
textinput.focus()
|
|
53
|
+
#setCursor = (index = undefined) => {
|
|
54
|
+
const empty = this.#textinput.value === ''
|
|
55
|
+
this.#textinput.focus()
|
|
55
56
|
|
|
56
|
-
if (
|
|
57
|
+
if (index === undefined) {
|
|
57
58
|
if (empty) {
|
|
58
59
|
index = 0
|
|
59
60
|
} else {
|
|
60
|
-
if (textinput.selectionEnd !== textinput.selectionStart) {
|
|
61
|
+
if (this.#textinput.selectionEnd !== this.#textinput.selectionStart) {
|
|
61
62
|
return
|
|
62
63
|
}
|
|
63
|
-
index = textinput.selectionEnd
|
|
64
|
+
index = this.#textinput.selectionEnd
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
const charbb = chardata[index]
|
|
68
|
+
const charbb = this.#chardata[index]
|
|
68
69
|
if (!empty) {
|
|
69
|
-
textinput.setSelectionRange(index, index)
|
|
70
|
+
this.#textinput.setSelectionRange(index, index)
|
|
70
71
|
}
|
|
71
|
-
cursor = getElement('text_cursor')
|
|
72
|
-
if (!cursor) {
|
|
73
|
-
cursor = document.createElementNS(NS.SVG, 'line')
|
|
74
|
-
assignAttributes(cursor, {
|
|
72
|
+
this.#cursor = getElement('text_cursor')
|
|
73
|
+
if (!this.#cursor) {
|
|
74
|
+
this.#cursor = document.createElementNS(NS.SVG, 'line')
|
|
75
|
+
assignAttributes(this.#cursor, {
|
|
75
76
|
id: 'text_cursor',
|
|
76
77
|
stroke: '#333',
|
|
77
78
|
'stroke-width': 1
|
|
78
79
|
})
|
|
79
|
-
getElement('selectorParentGroup').append(cursor)
|
|
80
|
+
getElement('selectorParentGroup').append(this.#cursor)
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
if (!blinker) {
|
|
83
|
-
blinker = setInterval(
|
|
84
|
-
const show = cursor.getAttribute('display') === 'none'
|
|
85
|
-
cursor.setAttribute('display', show ? 'inline' : 'none')
|
|
83
|
+
if (!this.#blinker) {
|
|
84
|
+
this.#blinker = setInterval(() => {
|
|
85
|
+
const show = this.#cursor.getAttribute('display') === 'none'
|
|
86
|
+
this.#cursor.setAttribute('display', show ? 'inline' : 'none')
|
|
86
87
|
}, 600)
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
const startPt = ptToScreen(charbb.x, textbb.y)
|
|
90
|
-
const endPt = ptToScreen(charbb.x, textbb.y + textbb.height)
|
|
90
|
+
const startPt = this.#ptToScreen(charbb.x, this.#textbb.y)
|
|
91
|
+
const endPt = this.#ptToScreen(charbb.x, this.#textbb.y + this.#textbb.height)
|
|
91
92
|
|
|
92
|
-
assignAttributes(cursor, {
|
|
93
|
+
assignAttributes(this.#cursor, {
|
|
93
94
|
x1: startPt.x,
|
|
94
95
|
y1: startPt.y,
|
|
95
96
|
x2: endPt.x,
|
|
@@ -98,8 +99,8 @@ export const textActionsMethod = (function () {
|
|
|
98
99
|
display: 'inline'
|
|
99
100
|
})
|
|
100
101
|
|
|
101
|
-
if (selblock) {
|
|
102
|
-
selblock.setAttribute('d', '')
|
|
102
|
+
if (this.#selblock) {
|
|
103
|
+
this.#selblock.setAttribute('d', '')
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -109,40 +110,41 @@ export const textActionsMethod = (function () {
|
|
|
109
110
|
* @param {Integer} end
|
|
110
111
|
* @param {boolean} skipInput
|
|
111
112
|
* @returns {void}
|
|
113
|
+
* @private
|
|
112
114
|
*/
|
|
113
|
-
|
|
115
|
+
#setSelection = (start, end, skipInput) => {
|
|
114
116
|
if (start === end) {
|
|
115
|
-
setCursor(end)
|
|
117
|
+
this.#setCursor(end)
|
|
116
118
|
return
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
if (!skipInput) {
|
|
120
|
-
textinput.setSelectionRange(start, end)
|
|
122
|
+
this.#textinput.setSelectionRange(start, end)
|
|
121
123
|
}
|
|
122
124
|
|
|
123
|
-
selblock = getElement('text_selectblock')
|
|
124
|
-
if (!selblock) {
|
|
125
|
-
selblock = document.createElementNS(NS.SVG, 'path')
|
|
126
|
-
assignAttributes(selblock, {
|
|
125
|
+
this.#selblock = getElement('text_selectblock')
|
|
126
|
+
if (!this.#selblock) {
|
|
127
|
+
this.#selblock = document.createElementNS(NS.SVG, 'path')
|
|
128
|
+
assignAttributes(this.#selblock, {
|
|
127
129
|
id: 'text_selectblock',
|
|
128
130
|
fill: 'green',
|
|
129
131
|
opacity: 0.5,
|
|
130
132
|
style: 'pointer-events:none'
|
|
131
133
|
})
|
|
132
|
-
getElement('selectorParentGroup').append(selblock)
|
|
134
|
+
getElement('selectorParentGroup').append(this.#selblock)
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
const startbb = chardata[start]
|
|
136
|
-
const endbb = chardata[end]
|
|
137
|
+
const startbb = this.#chardata[start]
|
|
138
|
+
const endbb = this.#chardata[end]
|
|
137
139
|
|
|
138
|
-
cursor.setAttribute('visibility', 'hidden')
|
|
140
|
+
this.#cursor.setAttribute('visibility', 'hidden')
|
|
139
141
|
|
|
140
|
-
const tl = ptToScreen(startbb.x, textbb.y)
|
|
141
|
-
const tr = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y)
|
|
142
|
-
const bl = ptToScreen(startbb.x, textbb.y + textbb.height)
|
|
143
|
-
const br = ptToScreen(
|
|
142
|
+
const tl = this.#ptToScreen(startbb.x, this.#textbb.y)
|
|
143
|
+
const tr = this.#ptToScreen(startbb.x + (endbb.x - startbb.x), this.#textbb.y)
|
|
144
|
+
const bl = this.#ptToScreen(startbb.x, this.#textbb.y + this.#textbb.height)
|
|
145
|
+
const br = this.#ptToScreen(
|
|
144
146
|
startbb.x + (endbb.x - startbb.x),
|
|
145
|
-
textbb.y + textbb.height
|
|
147
|
+
this.#textbb.y + this.#textbb.height
|
|
146
148
|
)
|
|
147
149
|
|
|
148
150
|
const dstr =
|
|
@@ -164,7 +166,7 @@ export const textActionsMethod = (function () {
|
|
|
164
166
|
bl.y +
|
|
165
167
|
'z'
|
|
166
168
|
|
|
167
|
-
assignAttributes(selblock, {
|
|
169
|
+
assignAttributes(this.#selblock, {
|
|
168
170
|
d: dstr,
|
|
169
171
|
display: 'inline'
|
|
170
172
|
})
|
|
@@ -175,29 +177,30 @@ export const textActionsMethod = (function () {
|
|
|
175
177
|
* @param {Float} mouseX
|
|
176
178
|
* @param {Float} mouseY
|
|
177
179
|
* @returns {Integer}
|
|
180
|
+
* @private
|
|
178
181
|
*/
|
|
179
|
-
|
|
182
|
+
#getIndexFromPoint = (mouseX, mouseY) => {
|
|
180
183
|
// Position cursor here
|
|
181
184
|
const pt = svgCanvas.getSvgRoot().createSVGPoint()
|
|
182
185
|
pt.x = mouseX
|
|
183
186
|
pt.y = mouseY
|
|
184
187
|
|
|
185
188
|
// No content, so return 0
|
|
186
|
-
if (chardata.length === 1) {
|
|
189
|
+
if (this.#chardata.length === 1) {
|
|
187
190
|
return 0
|
|
188
191
|
}
|
|
189
192
|
// Determine if cursor should be on left or right of character
|
|
190
|
-
let charpos = curtext.getCharNumAtPosition(pt)
|
|
193
|
+
let charpos = this.#curtext.getCharNumAtPosition(pt)
|
|
191
194
|
if (charpos < 0) {
|
|
192
195
|
// Out of text range, look at mouse coords
|
|
193
|
-
charpos = chardata.length - 2
|
|
194
|
-
if (mouseX <= chardata[0].x) {
|
|
196
|
+
charpos = this.#chardata.length - 2
|
|
197
|
+
if (mouseX <= this.#chardata[0].x) {
|
|
195
198
|
charpos = 0
|
|
196
199
|
}
|
|
197
|
-
} else if (charpos >= chardata.length - 2) {
|
|
198
|
-
charpos = chardata.length - 2
|
|
200
|
+
} else if (charpos >= this.#chardata.length - 2) {
|
|
201
|
+
charpos = this.#chardata.length - 2
|
|
199
202
|
}
|
|
200
|
-
const charbb = chardata[charpos]
|
|
203
|
+
const charbb = this.#chardata[charpos]
|
|
201
204
|
const mid = charbb.x + charbb.width / 2
|
|
202
205
|
if (mouseX > mid) {
|
|
203
206
|
charpos++
|
|
@@ -210,9 +213,10 @@ export const textActionsMethod = (function () {
|
|
|
210
213
|
* @param {Float} mouseX
|
|
211
214
|
* @param {Float} mouseY
|
|
212
215
|
* @returns {void}
|
|
216
|
+
* @private
|
|
213
217
|
*/
|
|
214
|
-
|
|
215
|
-
setCursor(getIndexFromPoint(mouseX, mouseY))
|
|
218
|
+
#setCursorFromPoint = (mouseX, mouseY) => {
|
|
219
|
+
this.#setCursor(this.#getIndexFromPoint(mouseX, mouseY))
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
/**
|
|
@@ -221,14 +225,15 @@ export const textActionsMethod = (function () {
|
|
|
221
225
|
* @param {Float} y
|
|
222
226
|
* @param {boolean} apply
|
|
223
227
|
* @returns {void}
|
|
228
|
+
* @private
|
|
224
229
|
*/
|
|
225
|
-
|
|
226
|
-
const i1 = textinput.selectionStart
|
|
227
|
-
const i2 = getIndexFromPoint(x, y)
|
|
230
|
+
#setEndSelectionFromPoint = (x, y, apply) => {
|
|
231
|
+
const i1 = this.#textinput.selectionStart
|
|
232
|
+
const i2 = this.#getIndexFromPoint(x, y)
|
|
228
233
|
|
|
229
234
|
const start = Math.min(i1, i2)
|
|
230
235
|
const end = Math.max(i1, i2)
|
|
231
|
-
setSelection(start, end, !apply)
|
|
236
|
+
this.#setSelection(start, end, !apply)
|
|
232
237
|
}
|
|
233
238
|
|
|
234
239
|
/**
|
|
@@ -236,8 +241,9 @@ export const textActionsMethod = (function () {
|
|
|
236
241
|
* @param {Float} xIn
|
|
237
242
|
* @param {Float} yIn
|
|
238
243
|
* @returns {module:math.XYObject}
|
|
244
|
+
* @private
|
|
239
245
|
*/
|
|
240
|
-
|
|
246
|
+
#screenToPt = (xIn, yIn) => {
|
|
241
247
|
const out = {
|
|
242
248
|
x: xIn,
|
|
243
249
|
y: yIn
|
|
@@ -246,8 +252,8 @@ export const textActionsMethod = (function () {
|
|
|
246
252
|
out.x /= zoom
|
|
247
253
|
out.y /= zoom
|
|
248
254
|
|
|
249
|
-
if (matrix) {
|
|
250
|
-
const pt = transformPoint(out.x, out.y, matrix.inverse())
|
|
255
|
+
if (this.#matrix) {
|
|
256
|
+
const pt = transformPoint(out.x, out.y, this.#matrix.inverse())
|
|
251
257
|
out.x = pt.x
|
|
252
258
|
out.y = pt.y
|
|
253
259
|
}
|
|
@@ -260,15 +266,16 @@ export const textActionsMethod = (function () {
|
|
|
260
266
|
* @param {Float} xIn
|
|
261
267
|
* @param {Float} yIn
|
|
262
268
|
* @returns {module:math.XYObject}
|
|
269
|
+
* @private
|
|
263
270
|
*/
|
|
264
|
-
|
|
271
|
+
#ptToScreen = (xIn, yIn) => {
|
|
265
272
|
const out = {
|
|
266
273
|
x: xIn,
|
|
267
274
|
y: yIn
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
if (matrix) {
|
|
271
|
-
const pt = transformPoint(out.x, out.y, matrix)
|
|
277
|
+
if (this.#matrix) {
|
|
278
|
+
const pt = transformPoint(out.x, out.y, this.#matrix)
|
|
272
279
|
out.x = pt.x
|
|
273
280
|
out.y = pt.y
|
|
274
281
|
}
|
|
@@ -283,279 +290,293 @@ export const textActionsMethod = (function () {
|
|
|
283
290
|
*
|
|
284
291
|
* @param {Event} evt
|
|
285
292
|
* @returns {void}
|
|
293
|
+
* @private
|
|
286
294
|
*/
|
|
287
|
-
|
|
288
|
-
setSelection(0, curtext.textContent.length)
|
|
289
|
-
evt.target.removeEventListener('click', selectAll)
|
|
295
|
+
#selectAll = (evt) => {
|
|
296
|
+
this.#setSelection(0, this.#curtext.textContent.length)
|
|
297
|
+
evt.target.removeEventListener('click', this.#selectAll)
|
|
290
298
|
}
|
|
291
299
|
|
|
292
300
|
/**
|
|
293
301
|
*
|
|
294
302
|
* @param {Event} evt
|
|
295
303
|
* @returns {void}
|
|
304
|
+
* @private
|
|
296
305
|
*/
|
|
297
|
-
|
|
298
|
-
if (!allowDbl || !curtext) {
|
|
306
|
+
#selectWord = (evt) => {
|
|
307
|
+
if (!this.#allowDbl || !this.#curtext) {
|
|
299
308
|
return
|
|
300
309
|
}
|
|
301
310
|
const zoom = svgCanvas.getZoom()
|
|
302
311
|
const ept = transformPoint(evt.pageX, evt.pageY, svgCanvas.getrootSctm())
|
|
303
312
|
const mouseX = ept.x * zoom
|
|
304
313
|
const mouseY = ept.y * zoom
|
|
305
|
-
const pt = screenToPt(mouseX, mouseY)
|
|
314
|
+
const pt = this.#screenToPt(mouseX, mouseY)
|
|
306
315
|
|
|
307
|
-
const index = getIndexFromPoint(pt.x, pt.y)
|
|
308
|
-
const str = curtext.textContent
|
|
309
|
-
const first = str.
|
|
310
|
-
const m = str.
|
|
316
|
+
const index = this.#getIndexFromPoint(pt.x, pt.y)
|
|
317
|
+
const str = this.#curtext.textContent
|
|
318
|
+
const first = str.slice(0, index).replace(/[a-z\d]+$/i, '').length
|
|
319
|
+
const m = str.slice(index).match(/^[a-z\d]+/i)
|
|
311
320
|
const last = (m ? m[0].length : 0) + index
|
|
312
|
-
setSelection(first, last)
|
|
321
|
+
this.#setSelection(first, last)
|
|
313
322
|
|
|
314
323
|
// Set tripleclick
|
|
315
|
-
svgCanvas.$click(evt.target, selectAll)
|
|
324
|
+
svgCanvas.$click(evt.target, this.#selectAll)
|
|
316
325
|
|
|
317
|
-
setTimeout(
|
|
318
|
-
evt.target.removeEventListener('click', selectAll)
|
|
326
|
+
setTimeout(() => {
|
|
327
|
+
evt.target.removeEventListener('click', this.#selectAll)
|
|
319
328
|
}, 300)
|
|
320
329
|
}
|
|
321
330
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
},
|
|
333
|
-
/**
|
|
334
|
-
* @param {Element} elem
|
|
335
|
-
* @returns {void}
|
|
336
|
-
*/
|
|
337
|
-
start (elem) {
|
|
338
|
-
curtext = elem
|
|
339
|
-
svgCanvas.textActions.toEditMode()
|
|
340
|
-
},
|
|
341
|
-
/**
|
|
342
|
-
* @param {external:MouseEvent} evt
|
|
343
|
-
* @param {Element} mouseTarget
|
|
344
|
-
* @param {Float} startX
|
|
345
|
-
* @param {Float} startY
|
|
346
|
-
* @returns {void}
|
|
347
|
-
*/
|
|
348
|
-
mouseDown (evt, mouseTarget, startX, startY) {
|
|
349
|
-
const pt = screenToPt(startX, startY)
|
|
350
|
-
|
|
351
|
-
textinput.focus()
|
|
352
|
-
setCursorFromPoint(pt.x, pt.y)
|
|
353
|
-
lastX = startX
|
|
354
|
-
lastY = startY
|
|
355
|
-
|
|
356
|
-
// TODO: Find way to block native selection
|
|
357
|
-
},
|
|
358
|
-
/**
|
|
359
|
-
* @param {Float} mouseX
|
|
360
|
-
* @param {Float} mouseY
|
|
361
|
-
* @returns {void}
|
|
362
|
-
*/
|
|
363
|
-
mouseMove (mouseX, mouseY) {
|
|
364
|
-
const pt = screenToPt(mouseX, mouseY)
|
|
365
|
-
setEndSelectionFromPoint(pt.x, pt.y)
|
|
366
|
-
},
|
|
367
|
-
/**
|
|
368
|
-
* @param {external:MouseEvent} evt
|
|
369
|
-
* @param {Float} mouseX
|
|
370
|
-
* @param {Float} mouseY
|
|
371
|
-
* @returns {void}
|
|
372
|
-
*/
|
|
373
|
-
mouseUp (evt, mouseX, mouseY) {
|
|
374
|
-
const pt = screenToPt(mouseX, mouseY)
|
|
375
|
-
|
|
376
|
-
setEndSelectionFromPoint(pt.x, pt.y, true)
|
|
377
|
-
|
|
378
|
-
// TODO: Find a way to make this work: Use transformed BBox instead of evt.target
|
|
379
|
-
// if (lastX === mouseX && lastY === mouseY
|
|
380
|
-
// && !rectsIntersect(transbb, {x: pt.x, y: pt.y, width: 0, height: 0})) {
|
|
381
|
-
// svgCanvas.textActions.toSelectMode(true);
|
|
382
|
-
// }
|
|
383
|
-
|
|
384
|
-
if (
|
|
385
|
-
evt.target !== curtext &&
|
|
386
|
-
mouseX < lastX + 2 &&
|
|
387
|
-
mouseX > lastX - 2 &&
|
|
388
|
-
mouseY < lastY + 2 &&
|
|
389
|
-
mouseY > lastY - 2
|
|
390
|
-
) {
|
|
391
|
-
svgCanvas.textActions.toSelectMode(true)
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
/**
|
|
395
|
-
* @function
|
|
396
|
-
* @param {Integer} index
|
|
397
|
-
* @returns {void}
|
|
398
|
-
*/
|
|
399
|
-
setCursor,
|
|
400
|
-
/**
|
|
401
|
-
* @param {Float} x
|
|
402
|
-
* @param {Float} y
|
|
403
|
-
* @returns {void}
|
|
404
|
-
*/
|
|
405
|
-
toEditMode (x, y) {
|
|
406
|
-
allowDbl = false
|
|
407
|
-
svgCanvas.setCurrentMode('textedit')
|
|
408
|
-
svgCanvas.selectorManager.requestSelector(curtext).showGrips(false)
|
|
409
|
-
// Make selector group accept clicks
|
|
410
|
-
/* const selector = */ svgCanvas.selectorManager.requestSelector(curtext) // Do we need this? Has side effect of setting lock, so keeping for now, but next line wasn't being used
|
|
411
|
-
// const sel = selector.selectorRect;
|
|
412
|
-
|
|
413
|
-
svgCanvas.textActions.init()
|
|
414
|
-
|
|
415
|
-
curtext.style.cursor = 'text'
|
|
416
|
-
|
|
417
|
-
// if (supportsEditableText()) {
|
|
418
|
-
// curtext.setAttribute('editable', 'simple');
|
|
419
|
-
// return;
|
|
420
|
-
// }
|
|
421
|
-
|
|
422
|
-
if (!arguments.length) {
|
|
423
|
-
setCursor()
|
|
424
|
-
} else {
|
|
425
|
-
const pt = screenToPt(x, y)
|
|
426
|
-
setCursorFromPoint(pt.x, pt.y)
|
|
427
|
-
}
|
|
331
|
+
/**
|
|
332
|
+
* @param {Element} target
|
|
333
|
+
* @param {Float} x
|
|
334
|
+
* @param {Float} y
|
|
335
|
+
* @returns {void}
|
|
336
|
+
*/
|
|
337
|
+
select (target, x, y) {
|
|
338
|
+
this.#curtext = target
|
|
339
|
+
svgCanvas.textActions.toEditMode(x, y)
|
|
340
|
+
}
|
|
428
341
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
*/
|
|
438
|
-
toSelectMode (selectElem) {
|
|
439
|
-
svgCanvas.setCurrentMode('select')
|
|
440
|
-
clearInterval(blinker)
|
|
441
|
-
blinker = null
|
|
442
|
-
if (selblock) {
|
|
443
|
-
selblock.setAttribute('display', 'none')
|
|
444
|
-
}
|
|
445
|
-
if (cursor) {
|
|
446
|
-
cursor.setAttribute('visibility', 'hidden')
|
|
447
|
-
}
|
|
448
|
-
curtext.style.cursor = 'move'
|
|
342
|
+
/**
|
|
343
|
+
* @param {Element} elem
|
|
344
|
+
* @returns {void}
|
|
345
|
+
*/
|
|
346
|
+
start (elem) {
|
|
347
|
+
this.#curtext = elem
|
|
348
|
+
svgCanvas.textActions.toEditMode()
|
|
349
|
+
}
|
|
449
350
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
351
|
+
/**
|
|
352
|
+
* @param {external:MouseEvent} evt
|
|
353
|
+
* @param {Element} mouseTarget
|
|
354
|
+
* @param {Float} startX
|
|
355
|
+
* @param {Float} startY
|
|
356
|
+
* @returns {void}
|
|
357
|
+
*/
|
|
358
|
+
mouseDown (evt, mouseTarget, startX, startY) {
|
|
359
|
+
const pt = this.#screenToPt(startX, startY)
|
|
453
360
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// No content, so delete
|
|
459
|
-
svgCanvas.deleteSelectedElements()
|
|
460
|
-
}
|
|
361
|
+
this.#textinput.focus()
|
|
362
|
+
this.#setCursorFromPoint(pt.x, pt.y)
|
|
363
|
+
this.#lastX = startX
|
|
364
|
+
this.#lastY = startY
|
|
461
365
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
366
|
+
// TODO: Find way to block native selection
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @param {Float} mouseX
|
|
371
|
+
* @param {Float} mouseY
|
|
372
|
+
* @returns {void}
|
|
373
|
+
*/
|
|
374
|
+
mouseMove (mouseX, mouseY) {
|
|
375
|
+
const pt = this.#screenToPt(mouseX, mouseY)
|
|
376
|
+
this.#setEndSelectionFromPoint(pt.x, pt.y)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* @param {external:MouseEvent} evt
|
|
381
|
+
* @param {Float} mouseX
|
|
382
|
+
* @param {Float} mouseY
|
|
383
|
+
* @returns {void}
|
|
384
|
+
*/
|
|
385
|
+
mouseUp (evt, mouseX, mouseY) {
|
|
386
|
+
const pt = this.#screenToPt(mouseX, mouseY)
|
|
387
|
+
|
|
388
|
+
this.#setEndSelectionFromPoint(pt.x, pt.y, true)
|
|
389
|
+
|
|
390
|
+
// TODO: Find a way to make this work: Use transformed BBox instead of evt.target
|
|
391
|
+
// if (lastX === mouseX && lastY === mouseY
|
|
392
|
+
// && !rectsIntersect(transbb, {x: pt.x, y: pt.y, width: 0, height: 0})) {
|
|
393
|
+
// svgCanvas.textActions.toSelectMode(true);
|
|
394
|
+
// }
|
|
395
|
+
|
|
396
|
+
if (
|
|
397
|
+
evt.target !== this.#curtext &&
|
|
398
|
+
mouseX < this.#lastX + 2 &&
|
|
399
|
+
mouseX > this.#lastX - 2 &&
|
|
400
|
+
mouseY < this.#lastY + 2 &&
|
|
401
|
+
mouseY > this.#lastY - 2
|
|
402
|
+
) {
|
|
403
|
+
svgCanvas.textActions.toSelectMode(true)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @param {Integer} index
|
|
409
|
+
* @returns {void}
|
|
410
|
+
*/
|
|
411
|
+
setCursor (index) {
|
|
412
|
+
this.#setCursor(index)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* @param {Float} x
|
|
417
|
+
* @param {Float} y
|
|
418
|
+
* @returns {void}
|
|
419
|
+
*/
|
|
420
|
+
toEditMode (x, y) {
|
|
421
|
+
this.#allowDbl = false
|
|
422
|
+
svgCanvas.setCurrentMode('textedit')
|
|
423
|
+
svgCanvas.selectorManager.requestSelector(this.#curtext).showGrips(false)
|
|
424
|
+
// Make selector group accept clicks
|
|
425
|
+
/* const selector = */ svgCanvas.selectorManager.requestSelector(this.#curtext) // Do we need this? Has side effect of setting lock, so keeping for now, but next line wasn't being used
|
|
426
|
+
// const sel = selector.selectorRect;
|
|
427
|
+
|
|
428
|
+
svgCanvas.textActions.init()
|
|
429
|
+
|
|
430
|
+
this.#curtext.style.cursor = 'text'
|
|
431
|
+
|
|
432
|
+
// if (supportsEditableText()) {
|
|
433
|
+
// curtext.setAttribute('editable', 'simple');
|
|
434
|
+
// return;
|
|
435
|
+
// }
|
|
436
|
+
|
|
437
|
+
if (arguments.length === 0) {
|
|
438
|
+
this.#setCursor()
|
|
439
|
+
} else {
|
|
440
|
+
const pt = this.#screenToPt(x, y)
|
|
441
|
+
this.#setCursorFromPoint(pt.x, pt.y)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
setTimeout(() => {
|
|
445
|
+
this.#allowDbl = true
|
|
446
|
+
}, 300)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @param {boolean|Element} selectElem
|
|
451
|
+
* @fires module:svgcanvas.SvgCanvas#event:selected
|
|
452
|
+
* @returns {void}
|
|
453
|
+
*/
|
|
454
|
+
toSelectMode (selectElem) {
|
|
455
|
+
svgCanvas.setCurrentMode('select')
|
|
456
|
+
clearInterval(this.#blinker)
|
|
457
|
+
this.#blinker = null
|
|
458
|
+
if (this.#selblock) {
|
|
459
|
+
this.#selblock.setAttribute('display', 'none')
|
|
460
|
+
}
|
|
461
|
+
if (this.#cursor) {
|
|
462
|
+
this.#cursor.setAttribute('visibility', 'hidden')
|
|
463
|
+
}
|
|
464
|
+
this.#curtext.style.cursor = 'move'
|
|
506
465
|
|
|
507
|
-
|
|
508
|
-
|
|
466
|
+
if (selectElem) {
|
|
467
|
+
svgCanvas.clearSelection()
|
|
468
|
+
this.#curtext.style.cursor = 'move'
|
|
509
469
|
|
|
510
|
-
|
|
470
|
+
svgCanvas.call('selected', [this.#curtext])
|
|
471
|
+
svgCanvas.addToSelection([this.#curtext], true)
|
|
472
|
+
}
|
|
473
|
+
if (!this.#curtext?.textContent.length) {
|
|
474
|
+
// No content, so delete
|
|
475
|
+
svgCanvas.deleteSelectedElements()
|
|
476
|
+
}
|
|
511
477
|
|
|
512
|
-
|
|
478
|
+
this.#textinput.blur()
|
|
513
479
|
|
|
514
|
-
|
|
480
|
+
this.#curtext = false
|
|
515
481
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
482
|
+
// if (supportsEditableText()) {
|
|
483
|
+
// curtext.removeAttribute('editable');
|
|
484
|
+
// }
|
|
485
|
+
}
|
|
519
486
|
|
|
520
|
-
|
|
521
|
-
|
|
487
|
+
/**
|
|
488
|
+
* @param {Element} elem
|
|
489
|
+
* @returns {void}
|
|
490
|
+
*/
|
|
491
|
+
setInputElem (elem) {
|
|
492
|
+
this.#textinput = elem
|
|
493
|
+
}
|
|
522
494
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
495
|
+
/**
|
|
496
|
+
* @returns {void}
|
|
497
|
+
*/
|
|
498
|
+
clear () {
|
|
499
|
+
if (svgCanvas.getCurrentMode() === 'textedit') {
|
|
500
|
+
svgCanvas.textActions.toSelectMode()
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* @param {Element} _inputElem Not in use
|
|
506
|
+
* @returns {void}
|
|
507
|
+
*/
|
|
508
|
+
init (_inputElem) {
|
|
509
|
+
if (!this.#curtext) {
|
|
510
|
+
return
|
|
511
|
+
}
|
|
512
|
+
let i
|
|
513
|
+
let end
|
|
514
|
+
// if (supportsEditableText()) {
|
|
515
|
+
// curtext.select();
|
|
516
|
+
// return;
|
|
517
|
+
// }
|
|
518
|
+
|
|
519
|
+
if (!this.#curtext.parentNode) {
|
|
520
|
+
// Result of the ffClone, need to get correct element
|
|
521
|
+
const selectedElements = svgCanvas.getSelectedElements()
|
|
522
|
+
this.#curtext = selectedElements[0]
|
|
523
|
+
svgCanvas.selectorManager.requestSelector(this.#curtext).showGrips(false)
|
|
524
|
+
}
|
|
526
525
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
end = curtext.getEndPositionOfChar(i)
|
|
526
|
+
const str = this.#curtext.textContent
|
|
527
|
+
const len = str.length
|
|
530
528
|
|
|
531
|
-
|
|
532
|
-
const zoom = svgCanvas.getZoom()
|
|
533
|
-
const offset = svgCanvas.contentW * zoom
|
|
534
|
-
start.x -= offset
|
|
535
|
-
end.x -= offset
|
|
529
|
+
const xform = this.#curtext.getAttribute('transform')
|
|
536
530
|
|
|
537
|
-
|
|
538
|
-
end.x /= zoom
|
|
539
|
-
}
|
|
531
|
+
this.#textbb = utilsGetBBox(this.#curtext)
|
|
540
532
|
|
|
541
|
-
|
|
542
|
-
// bbox data of the actual text for y, height purposes
|
|
533
|
+
this.#matrix = xform ? getMatrix(this.#curtext) : null
|
|
543
534
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
535
|
+
this.#chardata = []
|
|
536
|
+
this.#chardata.length = len
|
|
537
|
+
this.#textinput.focus()
|
|
538
|
+
|
|
539
|
+
this.#curtext.removeEventListener('dblclick', this.#selectWord)
|
|
540
|
+
this.#curtext.addEventListener('dblclick', this.#selectWord)
|
|
541
|
+
|
|
542
|
+
if (!len) {
|
|
543
|
+
end = { x: this.#textbb.x + this.#textbb.width / 2, width: 0 }
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
for (i = 0; i < len; i++) {
|
|
547
|
+
const start = this.#curtext.getStartPositionOfChar(i)
|
|
548
|
+
end = this.#curtext.getEndPositionOfChar(i)
|
|
549
|
+
|
|
550
|
+
if (!supportsGoodTextCharPos()) {
|
|
551
|
+
const zoom = svgCanvas.getZoom()
|
|
552
|
+
const offset = svgCanvas.contentW * zoom
|
|
553
|
+
start.x -= offset
|
|
554
|
+
end.x -= offset
|
|
555
|
+
|
|
556
|
+
start.x /= zoom
|
|
557
|
+
end.x /= zoom
|
|
551
558
|
}
|
|
552
559
|
|
|
553
|
-
//
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
560
|
+
// Get a "bbox" equivalent for each character. Uses the
|
|
561
|
+
// bbox data of the actual text for y, height purposes
|
|
562
|
+
|
|
563
|
+
// TODO: Decide if y, width and height are actually necessary
|
|
564
|
+
this.#chardata[i] = {
|
|
565
|
+
x: start.x,
|
|
566
|
+
y: this.#textbb.y, // start.y?
|
|
567
|
+
width: end.x - start.x,
|
|
568
|
+
height: this.#textbb.height
|
|
569
|
+
}
|
|
559
570
|
}
|
|
571
|
+
|
|
572
|
+
// Add a last bbox for cursor at end of text
|
|
573
|
+
this.#chardata.push({
|
|
574
|
+
x: end.x,
|
|
575
|
+
width: 0
|
|
576
|
+
})
|
|
577
|
+
this.#setSelection(this.#textinput.selectionStart, this.#textinput.selectionEnd, true)
|
|
560
578
|
}
|
|
561
|
-
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Export singleton instance for backward compatibility
|
|
582
|
+
export const textActionsMethod = new TextActions()
|