fix: Enhance text properties for multiple selected elements (SVG-Edit/svgedit#918) (#919)

* fix: Enhance text properties for multiple selected elements (SVG-Edit/svgedit#918)

This commit addresses issue SVG-Edit/svgedit#918 by improving
text property handling when multiple elements are selected.
You can now set Bold, Italic, Text-Decoration, Text-Anchor,
Letter-Spacing, Word-Spacing, Text Length, and Length Adjust.

* Update Editor.js

---------

Co-authored-by: JFH <20402845+jfhenon@users.noreply.github.com>
master
Mo'ath Zaghdad 2023-09-18 13:38:41 +03:00 committed by GitHub
parent 24b8f74c4d
commit 0895235c17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 91 deletions

View File

@ -526,6 +526,7 @@ const setPaintMethod = (type, paint) => {
break break
} }
} }
/** /**
* Sets the stroke width for the current selected elements. * Sets the stroke width for the current selected elements.
* When attempting to set a line's width to 0, this changes it to 1 instead. * When attempting to set a line's width to 0, this changes it to 1 instead.
@ -599,54 +600,41 @@ const setStrokeAttrMethod = (attr, val) => {
svgCanvas.call('changed', selectedElements) svgCanvas.call('changed', selectedElements)
} }
} }
/** /**
* Check whether selected element is bold or not. * Check if all selected text elements are in bold.
* @function module:svgcanvas.SvgCanvas#getBold * @function module:svgcanvas.SvgCanvas#getBold
* @returns {boolean} Indicates whether or not element is bold * @returns {boolean} `true` if all selected elements are bold, `false` otherwise.
*/ */
const getBoldMethod = () => { const getBoldMethod = () => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
// should only have one element selected const textElements = selectedElements.filter(el => el?.tagName === 'text')
const selected = selectedElements[0] return textElements.every(el => el.getAttribute('font-weight') === 'bold')
if (selected?.tagName === 'text' &&
!selectedElements[1]) {
return (selected.getAttribute('font-weight') === 'bold')
}
return false
} }
/** /**
* Make the selected element bold or normal. * Make the selected element(s) bold or normal.
* @function module:svgcanvas.SvgCanvas#setBold * @function module:svgcanvas.SvgCanvas#setBold
* @param {boolean} b - Indicates bold (`true`) or normal (`false`) * @param {boolean} b - Indicates bold (`true`) or normal (`false`)
* @returns {void} * @returns {void}
*/ */
const setBoldMethod = (b) => { const setBoldMethod = (b) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && svgCanvas.changeSelectedAttribute('font-weight', b ? 'bold' : 'normal', textElements)
!selectedElements[1]) { if (!textElements.some(el => el.textContent)) {
svgCanvas.changeSelectedAttribute('font-weight', b ? 'bold' : 'normal')
}
if (!selectedElements[0].textContent) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
/** /**
* Check whether selected element has the given text decoration value or not. * Check if all selected text elements have the given text decoration value or not.
* @returns {boolean} Indicates whether or not element has the text decoration value * @returns {boolean} Indicates whether or not elements have the text decoration value
*/ */
const hasTextDecorationMethod = (value) => { const hasTextDecorationMethod = (value) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
return textElements.every(el => (el.getAttribute('text-decoration') || '').includes(value))
if (selected?.tagName === 'text' && !selectedElements[1]) {
const attribute = selected.getAttribute('text-decoration') || ''
return attribute.includes(value)
}
return false
} }
/** /**
@ -655,13 +643,24 @@ const hasTextDecorationMethod = (value) => {
* @returns {void} * @returns {void}
*/ */
const addTextDecorationMethod = (value) => { const addTextDecorationMethod = (value) => {
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) {
const oldValue = selected.getAttribute('text-decoration') || '' const batchCmd = new BatchCommand()
svgCanvas.changeSelectedAttribute('text-decoration', (oldValue + ' ' + value).trim()) textElements.forEach(elem => {
const oldValue = elem.getAttribute('text-decoration') || ''
// Add the new text decoration value if it did not exist
if (!oldValue.includes(value)) {
batchCmd.addSubCommand(new ChangeElementCommand(elem, { 'text-decoration': oldValue }))
svgCanvas.changeSelectedAttributeNoUndo('text-decoration', (oldValue + ' ' + value).trim(), [elem])
} }
if (selectedElements.length > 0 && !selectedElements[0].textContent) { })
if (!batchCmd.isEmpty()) {
svgCanvas.undoMgr.addCommandToHistory(batchCmd)
}
if (!textElements.some(el => el.textContent)) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
@ -672,44 +671,47 @@ const addTextDecorationMethod = (value) => {
* @returns {void} * @returns {void}
*/ */
const removeTextDecorationMethod = (value) => { const removeTextDecorationMethod = (value) => {
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) {
const actualValues = selected.getAttribute('text-decoration') || '' const batchCmd = new BatchCommand()
svgCanvas.changeSelectedAttribute('text-decoration', actualValues.replace(value, '').trim()) textElements.forEach(elem => {
const actualValues = elem.getAttribute('text-decoration') || ''
batchCmd.addSubCommand(new ChangeElementCommand(elem, { 'text-decoration': actualValues }))
svgCanvas.changeSelectedAttributeNoUndo('text-decoration', actualValues.replace(value, '').trim(), [elem])
})
if (!batchCmd.isEmpty()) {
svgCanvas.undoMgr.addCommandToHistory(batchCmd)
} }
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
if (!textElements.some(el => el.textContent)) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
/** /**
* Check whether selected element is in italics or not. * Check if all selected elements have an italic font style.
* @function module:svgcanvas.SvgCanvas#getItalic * @function module:svgcanvas.SvgCanvas#getItalic
* @returns {boolean} Indicates whether or not element is italic * @returns {boolean} `true` if all selected elements are in italics, `false` otherwise.
*/ */
const getItalicMethod = () => { const getItalicMethod = () => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { return textElements.every(el => el.getAttribute('font-style') === 'italic')
return (selected.getAttribute('font-style') === 'italic')
}
return false
} }
/** /**
* Make the selected element italic or normal. * Make the selected element(s) italic or normal.
* @function module:svgcanvas.SvgCanvas#setItalic * @function module:svgcanvas.SvgCanvas#setItalic
* @param {boolean} i - Indicates italic (`true`) or normal (`false`) * @param {boolean} i - Indicates italic (`true`) or normal (`false`)
* @returns {void} * @returns {void}
*/ */
const setItalicMethod = (i) => { const setItalicMethod = (i) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { svgCanvas.changeSelectedAttribute('font-style', i ? 'italic' : 'normal', textElements)
svgCanvas.changeSelectedAttribute('font-style', i ? 'italic' : 'normal') if (!textElements.some(el => el.textContent)) {
}
if (!selectedElements[0].textContent) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
@ -721,13 +723,8 @@ const setItalicMethod = (i) => {
*/ */
const setTextAnchorMethod = (value) => { const setTextAnchorMethod = (value) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { svgCanvas.changeSelectedAttribute('text-anchor', value, textElements)
svgCanvas.changeSelectedAttribute('text-anchor', value)
}
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
svgCanvas.textActions.setCursor()
}
} }
/** /**
@ -737,11 +734,9 @@ const setTextAnchorMethod = (value) => {
*/ */
const setLetterSpacingMethod = (value) => { const setLetterSpacingMethod = (value) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { svgCanvas.changeSelectedAttribute('letter-spacing', value, textElements)
svgCanvas.changeSelectedAttribute('letter-spacing', value) if (!textElements.some(el => el.textContent)) {
}
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
@ -753,11 +748,9 @@ const setLetterSpacingMethod = (value) => {
*/ */
const setWordSpacingMethod = (value) => { const setWordSpacingMethod = (value) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { svgCanvas.changeSelectedAttribute('word-spacing', value, textElements)
svgCanvas.changeSelectedAttribute('word-spacing', value) if (!textElements.some(el => el.textContent)) {
}
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
@ -769,11 +762,9 @@ const setWordSpacingMethod = (value) => {
*/ */
const setTextLengthMethod = (value) => { const setTextLengthMethod = (value) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { svgCanvas.changeSelectedAttribute('textLength', value, textElements)
svgCanvas.changeSelectedAttribute('textLength', value) if (!textElements.some(el => el.textContent)) {
}
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
@ -785,11 +776,9 @@ const setTextLengthMethod = (value) => {
*/ */
const setLengthAdjustMethod = (value) => { const setLengthAdjustMethod = (value) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const selected = selectedElements[0] const textElements = selectedElements.filter(el => el?.tagName === 'text')
if (selected?.tagName === 'text' && !selectedElements[1]) { svgCanvas.changeSelectedAttribute('lengthAdjust', value, textElements)
svgCanvas.changeSelectedAttribute('lengthAdjust', value) if (!textElements.some(el => el.textContent)) {
}
if (selectedElements.length > 0 && !selectedElements[0].textContent) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }
@ -810,9 +799,10 @@ const getFontFamilyMethod = () => {
*/ */
const setFontFamilyMethod = (val) => { const setFontFamilyMethod = (val) => {
const selectedElements = svgCanvas.getSelectedElements() const selectedElements = svgCanvas.getSelectedElements()
const textElements = selectedElements.filter(el => el?.tagName === 'text')
svgCanvas.setCurText('font_family', val) svgCanvas.setCurText('font_family', val)
svgCanvas.changeSelectedAttribute('font-family', val) svgCanvas.changeSelectedAttribute('font-family', val, textElements)
if (!selectedElements[0]?.textContent) { if (!textElements.some(el => el.textContent)) {
svgCanvas.textActions.setCursor() svgCanvas.textActions.setCursor()
} }
} }

View File

@ -470,7 +470,7 @@ class Editor extends EditorStartup {
const isNode = mode === 'pathedit' const isNode = mode === 'pathedit'
// if this.elems[1] is present, then we have more than one element // if this.elems[1] is present, then we have more than one element
this.selectedElement = elems.length === 1 || !elems[1] ? elems[0] : null this.selectedElement = elems.length === 1 || !elems[1] ? elems[0] : null
this.multiselected = elems.length >= 2 && elems[1] this.multiselected = elems.length >= 2 && !!elems[1]
if (this.selectedElement && !isNode) { if (this.selectedElement && !isNode) {
this.topPanel.update() this.topPanel.update()
} // if (elem) } // if (elem)