diff --git a/.vscode/settings.json b/.vscode/settings.json index 02034a7..213b9d5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,6 +24,7 @@ "rowspan", "TEXTLIKE", "trlist", + "updown", "vite", "vitepress", "Yahei" diff --git a/src/editor/core/event/handlers/keydown/backspace.ts b/src/editor/core/event/handlers/keydown/backspace.ts new file mode 100644 index 0000000..044ac15 --- /dev/null +++ b/src/editor/core/event/handlers/keydown/backspace.ts @@ -0,0 +1,60 @@ +import { ZERO } from '../../../../dataset/constant/Common' +import { CanvasEvent } from '../../CanvasEvent' + +export function backspace(evt: KeyboardEvent, host: CanvasEvent) { + const draw = host.getDraw() + const isReadonly = draw.isReadonly() + if (isReadonly) return + // 控件整体性验证 + const control = draw.getControl() + const activeControl = control.getActiveControl() + if (control.isPartRangeInControlOutside()) return + const rangeManager = draw.getRange() + let curIndex: number | null + if (activeControl) { + // 光标在控件内 + curIndex = control.keydown(evt) + } else { + // 普通元素删除 + const position = draw.getPosition() + const cursorPosition = position.getCursorPosition() + if (!cursorPosition) return + const { index } = cursorPosition + const { startIndex, endIndex } = rangeManager.getRange() + const isCollapsed = rangeManager.getIsCollapsed() + const elementList = draw.getElementList() + // 判断是否允许删除 + if (isCollapsed && index === 0) { + const firstElement = elementList[index] + if (firstElement.value === ZERO) { + // 取消首字符列表设置 + if (firstElement.listId) { + draw.getListParticle().unsetList() + } + evt.preventDefault() + return + } + } + // 清空当前行对齐方式 + const startElement = elementList[startIndex] + if (isCollapsed && startElement.rowFlex && startElement.value === ZERO) { + const rowList = draw.getRowList() + const positionList = position.getPositionList() + const rowNo = positionList[startIndex].rowNo + const rowFlexElementList = rowList[rowNo].elementList + rowFlexElementList.forEach(element => { + delete element.rowFlex + }) + } + if (!isCollapsed) { + draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex) + } else { + draw.spliceElementList(elementList, index, 1) + } + curIndex = isCollapsed ? index - 1 : startIndex + } + if (curIndex === null) return + draw.getGlobalEvent().setCanvasEventAbility() + rangeManager.setRange(curIndex, curIndex) + draw.render({ curIndex }) +} diff --git a/src/editor/core/event/handlers/keydown/delete.ts b/src/editor/core/event/handlers/keydown/delete.ts new file mode 100644 index 0000000..b831b51 --- /dev/null +++ b/src/editor/core/event/handlers/keydown/delete.ts @@ -0,0 +1,39 @@ +import { CanvasEvent } from '../../CanvasEvent' + +export function del(evt: KeyboardEvent, host: CanvasEvent) { + const draw = host.getDraw() + const isReadonly = draw.isReadonly() + if (isReadonly) return + // 控件整体性验证 + const control = draw.getControl() + const activeControl = control.getActiveControl() + if (control.isPartRangeInControlOutside()) return + const rangeManager = draw.getRange() + const { startIndex, endIndex } = rangeManager.getRange() + const elementList = draw.getElementList() + let curIndex: number | null + if (activeControl) { + // 光标在控件内 + curIndex = control.keydown(evt) + } else if (elementList[endIndex + 1]?.controlId) { + // 光标在控件前 + curIndex = control.removeControl(endIndex + 1) + } else { + // 普通元素 + const position = draw.getPosition() + const cursorPosition = position.getCursorPosition() + if (!cursorPosition) return + const { index } = cursorPosition + const isCollapsed = rangeManager.getIsCollapsed() + if (!isCollapsed) { + draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex) + } else { + draw.spliceElementList(elementList, index + 1, 1) + } + curIndex = isCollapsed ? index : startIndex + } + if (curIndex === null) return + draw.getGlobalEvent().setCanvasEventAbility() + rangeManager.setRange(curIndex, curIndex) + draw.render({ curIndex }) +} diff --git a/src/editor/core/event/handlers/keydown/index.ts b/src/editor/core/event/handlers/keydown/index.ts index 2cf031e..f5e1d45 100644 --- a/src/editor/core/event/handlers/keydown/index.ts +++ b/src/editor/core/event/handlers/keydown/index.ts @@ -1,99 +1,23 @@ -import { ZERO } from '../../../../dataset/constant/Common' import { EditorMode, EditorZone } from '../../../../dataset/enum/Editor' -import { ElementType } from '../../../../dataset/enum/Element' import { KeyMap } from '../../../../dataset/enum/KeyMap' -import { MoveDirection } from '../../../../dataset/enum/Observer' -import { IElement, IElementPosition } from '../../../../interface/Element' -import { formatElementContext } from '../../../../utils/element' import { isMod } from '../../../../utils/hotkey' import { CanvasEvent } from '../../CanvasEvent' +import { backspace } from './backspace' +import { del } from './delete' import { enter } from './enter' import { left } from './left' import { right } from './right' +import { tab } from './tab' +import { updown } from './updown' export function keydown(evt: KeyboardEvent, host: CanvasEvent) { if (host.isComposing) return const draw = host.getDraw() - const position = draw.getPosition() - const cursorPosition = position.getCursorPosition() - if (!cursorPosition) return - const isReadonly = draw.isReadonly() - const historyManager = draw.getHistoryManager() - const elementList = draw.getElementList() - const positionList = position.getPositionList() - const { index } = cursorPosition - const rangeManager = draw.getRange() - const { startIndex, endIndex } = rangeManager.getRange() - const isCollapsed = startIndex === endIndex - // 当前激活控件 - const control = draw.getControl() - const activeControl = control.getActiveControl() + // 键盘事件逻辑分发 if (evt.key === KeyMap.Backspace) { - if (isReadonly || control.isPartRangeInControlOutside()) return - let curIndex: number | null - if (activeControl) { - curIndex = control.keydown(evt) - } else { - // 判断是否允许删除 - if (isCollapsed && index === 0) { - const firstElement = elementList[index] - if (firstElement.value === ZERO) { - // 取消首字符列表设置 - if (firstElement.listId) { - draw.getListParticle().unsetList() - } - evt.preventDefault() - return - } - } - // 清空当前行对齐方式 - const startElement = elementList[startIndex] - if (isCollapsed && startElement.rowFlex && startElement.value === ZERO) { - const rowList = draw.getRowList() - const rowNo = positionList[startIndex].rowNo - const rowFlexElementList = rowList[rowNo].elementList - rowFlexElementList.forEach(element => { - delete element.rowFlex - }) - } - if (!isCollapsed) { - draw.spliceElementList( - elementList, - startIndex + 1, - endIndex - startIndex - ) - } else { - draw.spliceElementList(elementList, index, 1) - } - curIndex = isCollapsed ? index - 1 : startIndex - } - if (curIndex === null) return - draw.getGlobalEvent().setCanvasEventAbility() - rangeManager.setRange(curIndex, curIndex) - draw.render({ curIndex }) + backspace(evt, host) } else if (evt.key === KeyMap.Delete) { - if (isReadonly || control.isPartRangeInControlOutside()) return - let curIndex: number | null - if (activeControl) { - curIndex = control.keydown(evt) - } else if (elementList[endIndex + 1]?.controlId) { - curIndex = control.removeControl(endIndex + 1) - } else { - if (!isCollapsed) { - draw.spliceElementList( - elementList, - startIndex + 1, - endIndex - startIndex - ) - } else { - draw.spliceElementList(elementList, index + 1, 1) - } - curIndex = isCollapsed ? index : startIndex - } - if (curIndex === null) return - draw.getGlobalEvent().setCanvasEventAbility() - rangeManager.setRange(curIndex, curIndex) - draw.render({ curIndex }) + del(evt, host) } else if (evt.key === KeyMap.Enter) { enter(evt, host) } else if (evt.key === KeyMap.Left) { @@ -101,124 +25,14 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) { } else if (evt.key === KeyMap.Right) { right(evt, host) } else if (evt.key === KeyMap.Up || evt.key === KeyMap.Down) { - if (isReadonly) return - let anchorPosition: IElementPosition = cursorPosition - // 扩大选区时,判断移动光标点 - if (evt.shiftKey) { - if (startIndex === cursorPosition.index) { - anchorPosition = positionList[endIndex] - } else { - anchorPosition = positionList[startIndex] - } - } - const { - index, - rowNo, - rowIndex, - coordinate: { - leftTop: [curLeftX], - rightTop: [curRightX] - } - } = anchorPosition - // 向上时在首行、向下时在尾行则忽略 - const isUp = evt.key === KeyMap.Up - if ( - (isUp && rowIndex === 0) || - (!isUp && rowIndex === draw.getRowCount() - 1) - ) { - return - } - // 查找下一行位置列表 - const probablePosition: IElementPosition[] = [] - if (isUp) { - let p = index - 1 - while (p > 0) { - const position = positionList[p] - p-- - if (position.rowNo === rowNo) continue - if ( - probablePosition[0] && - probablePosition[0].rowNo !== position.rowNo - ) { - break - } - probablePosition.unshift(position) - } - } else { - let p = index + 1 - while (p < positionList.length) { - const position = positionList[p] - p++ - if (position.rowNo === rowNo) continue - if ( - probablePosition[0] && - probablePosition[0].rowNo !== position.rowNo - ) { - break - } - probablePosition.push(position) - } - } - // 查找下一行位置:第一个存在交叉宽度的元素位置 - let nextIndex = 0 - for (let p = 0; p < probablePosition.length; p++) { - const nextPosition = probablePosition[p] - const { - coordinate: { - leftTop: [nextLeftX], - rightTop: [nextRightX] - } - } = nextPosition - if (p === probablePosition.length - 1) { - nextIndex = nextPosition.index - } - if (curRightX <= nextLeftX || curLeftX >= nextRightX) continue - nextIndex = nextPosition.index - break - } - if (!nextIndex) return - // shift则缩放选区 - let anchorStartIndex = nextIndex - let anchorEndIndex = nextIndex - if (evt.shiftKey) { - if (startIndex !== endIndex) { - if (startIndex === cursorPosition.index) { - anchorStartIndex = startIndex - } else { - anchorEndIndex = endIndex - } - } else { - if (isUp) { - anchorEndIndex = endIndex - } else { - anchorStartIndex = startIndex - } - } - } - if (anchorStartIndex > anchorEndIndex) { - // prettier-ignore - [anchorStartIndex, anchorEndIndex] = [anchorEndIndex, anchorStartIndex] - } - rangeManager.setRange(anchorStartIndex, anchorEndIndex) - const isCollapsed = anchorStartIndex === anchorEndIndex - draw.render({ - curIndex: isCollapsed ? anchorStartIndex : undefined, - isSetCursor: isCollapsed, - isSubmitHistory: false, - isCompute: false - }) - // 将光标移动到可视范围内 - draw.getCursor().moveCursorToVisible({ - cursorPosition: positionList[isUp ? anchorStartIndex : anchorEndIndex], - direction: isUp ? MoveDirection.UP : MoveDirection.DOWN - }) + updown(evt, host) } else if (isMod(evt) && evt.key === KeyMap.Z) { - if (isReadonly && draw.getMode() !== EditorMode.FORM) return - historyManager.undo() + if (draw.isReadonly() && draw.getMode() !== EditorMode.FORM) return + draw.getHistoryManager().undo() evt.preventDefault() } else if (isMod(evt) && evt.key === KeyMap.Y) { - if (isReadonly && draw.getMode() !== EditorMode.FORM) return - historyManager.redo() + if (draw.isReadonly() && draw.getMode() !== EditorMode.FORM) return + draw.getHistoryManager().redo() evt.preventDefault() } else if (isMod(evt) && evt.key === KeyMap.C) { host.copy() @@ -230,7 +44,7 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) { host.selectAll() evt.preventDefault() } else if (isMod(evt) && evt.key === KeyMap.S) { - if (isReadonly) return + if (draw.isReadonly()) return const listener = draw.getListener() if (listener.saved) { listener.saved(draw.getValue()) @@ -250,12 +64,6 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) { } evt.preventDefault() } else if (evt.key === KeyMap.TAB) { - const tabElement: IElement = { - type: ElementType.TAB, - value: '' - } - formatElementContext(elementList, [tabElement], startIndex) - draw.insertElementList([tabElement]) - evt.preventDefault() + tab(evt, host) } } diff --git a/src/editor/core/event/handlers/keydown/tab.ts b/src/editor/core/event/handlers/keydown/tab.ts new file mode 100644 index 0000000..b1f75c6 --- /dev/null +++ b/src/editor/core/event/handlers/keydown/tab.ts @@ -0,0 +1,20 @@ +import { ElementType } from '../../../../dataset/enum/Element' +import { IElement } from '../../../../interface/Element' +import { formatElementContext } from '../../../../utils/element' +import { CanvasEvent } from '../../CanvasEvent' + +export function tab(evt: KeyboardEvent, host: CanvasEvent) { + const draw = host.getDraw() + const isReadonly = draw.isReadonly() + if (isReadonly) return + const tabElement: IElement = { + type: ElementType.TAB, + value: '' + } + const rangeManager = draw.getRange() + const { startIndex } = rangeManager.getRange() + const elementList = draw.getElementList() + formatElementContext(elementList, [tabElement], startIndex) + draw.insertElementList([tabElement]) + evt.preventDefault() +} diff --git a/src/editor/core/event/handlers/keydown/updown.ts b/src/editor/core/event/handlers/keydown/updown.ts new file mode 100644 index 0000000..222bd36 --- /dev/null +++ b/src/editor/core/event/handlers/keydown/updown.ts @@ -0,0 +1,120 @@ +import { KeyMap } from '../../../../dataset/enum/KeyMap' +import { MoveDirection } from '../../../../dataset/enum/Observer' +import { IElementPosition } from '../../../../interface/Element' +import { CanvasEvent } from '../../CanvasEvent' + +export function updown(evt: KeyboardEvent, host: CanvasEvent) { + const draw = host.getDraw() + const isReadonly = draw.isReadonly() + if (isReadonly) return + const position = draw.getPosition() + const cursorPosition = position.getCursorPosition() + if (!cursorPosition) return + const rangeManager = draw.getRange() + const { startIndex, endIndex } = rangeManager.getRange() + const positionList = position.getPositionList() + let anchorPosition: IElementPosition = cursorPosition + // 扩大选区时,判断移动光标点 + if (evt.shiftKey) { + if (startIndex === cursorPosition.index) { + anchorPosition = positionList[endIndex] + } else { + anchorPosition = positionList[startIndex] + } + } + const { + index, + rowNo, + rowIndex, + coordinate: { + leftTop: [curLeftX], + rightTop: [curRightX] + } + } = anchorPosition + // 向上时在首行、向下时在尾行则忽略 + const isUp = evt.key === KeyMap.Up + if ( + (isUp && rowIndex === 0) || + (!isUp && rowIndex === draw.getRowCount() - 1) + ) { + return + } + // 查找下一行位置列表 + const probablePosition: IElementPosition[] = [] + if (isUp) { + let p = index - 1 + while (p > 0) { + const position = positionList[p] + p-- + if (position.rowNo === rowNo) continue + if (probablePosition[0] && probablePosition[0].rowNo !== position.rowNo) { + break + } + probablePosition.unshift(position) + } + } else { + let p = index + 1 + while (p < positionList.length) { + const position = positionList[p] + p++ + if (position.rowNo === rowNo) continue + if (probablePosition[0] && probablePosition[0].rowNo !== position.rowNo) { + break + } + probablePosition.push(position) + } + } + // 查找下一行位置:第一个存在交叉宽度的元素位置 + let nextIndex = 0 + for (let p = 0; p < probablePosition.length; p++) { + const nextPosition = probablePosition[p] + const { + coordinate: { + leftTop: [nextLeftX], + rightTop: [nextRightX] + } + } = nextPosition + if (p === probablePosition.length - 1) { + nextIndex = nextPosition.index + } + if (curRightX <= nextLeftX || curLeftX >= nextRightX) continue + nextIndex = nextPosition.index + break + } + if (!nextIndex) return + // shift则缩放选区 + let anchorStartIndex = nextIndex + let anchorEndIndex = nextIndex + if (evt.shiftKey) { + if (startIndex !== endIndex) { + if (startIndex === cursorPosition.index) { + anchorStartIndex = startIndex + } else { + anchorEndIndex = endIndex + } + } else { + if (isUp) { + anchorEndIndex = endIndex + } else { + anchorStartIndex = startIndex + } + } + } + if (anchorStartIndex > anchorEndIndex) { + // prettier-ignore + [anchorStartIndex, anchorEndIndex] = [anchorEndIndex, anchorStartIndex] + } + rangeManager.setRange(anchorStartIndex, anchorEndIndex) + const isCollapsed = anchorStartIndex === anchorEndIndex + draw.render({ + curIndex: isCollapsed ? anchorStartIndex : undefined, + isSetCursor: isCollapsed, + isSubmitHistory: false, + isCompute: false + }) + // 将光标移动到可视范围内 + draw.getCursor().moveCursorToVisible({ + cursorPosition: positionList[isUp ? anchorStartIndex : anchorEndIndex], + direction: isUp ? MoveDirection.UP : MoveDirection.DOWN + }) +}