From 3d71e627eb94e25f221f08981e3713335ad1b328 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 15 Aug 2017 18:44:23 +0200 Subject: [PATCH] Move cursors container inside lines container This will ensure that applying any style that changes the location of the lines container will also correctly position the cursors. --- spec/text-editor-component-spec.js | 33 +++++++- src/text-editor-component.js | 116 +++++++++++++++-------------- 2 files changed, 88 insertions(+), 61 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index cbd6f205b..1fd2d7f77 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -220,13 +220,13 @@ describe('TextEditorComponent', () => { it('keeps the number of tiles stable when the visible line count changes during vertical scrolling', async () => { const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) await setEditorHeightInLines(component, 5.5) - expect(component.refs.lineTiles.children.length).toBe(3) + expect(component.refs.lineTiles.children.length).toBe(3 + 1) // account for cursors container await setScrollTop(component, 0.5 * component.getLineHeight()) - expect(component.refs.lineTiles.children.length).toBe(3) + expect(component.refs.lineTiles.children.length).toBe(3 + 1) // account for cursors container await setScrollTop(component, 1 * component.getLineHeight()) - expect(component.refs.lineTiles.children.length).toBe(3) + expect(component.refs.lineTiles.children.length).toBe(3 + 1) // account for cursors container }) it('recycles tiles on resize', async () => { @@ -479,7 +479,6 @@ describe('TextEditorComponent', () => { editor.setCursorScreenPosition([0, 3]) await component.getNextUpdatePromise() - const cursor = element.querySelector('.cursor') verifyCursorPosition(component, element.querySelector('.cursor'), 0, 3) editor.setCursorScreenPosition([0, 4]) @@ -487,6 +486,32 @@ describe('TextEditorComponent', () => { verifyCursorPosition(component, element.querySelector('.cursor'), 0, 4) }) + it('positions cursors correctly when the lines container has a margin and/or is padded', async () => { + const {component, element, editor} = buildComponent() + + component.refs.lineTiles.style.marginLeft = '10px' + TextEditor.didUpdateStyles() + await component.getNextUpdatePromise() + + editor.setCursorBufferPosition([0, 3]) + await component.getNextUpdatePromise() + verifyCursorPosition(component, element.querySelector('.cursor'), 0, 3) + + editor.setCursorScreenPosition([1, 0]) + await component.getNextUpdatePromise() + verifyCursorPosition(component, element.querySelector('.cursor'), 1, 0) + + component.refs.lineTiles.style.paddingTop = '5px' + TextEditor.didUpdateStyles() + await component.getNextUpdatePromise() + verifyCursorPosition(component, element.querySelector('.cursor'), 1, 0) + + editor.setCursorScreenPosition([2, 2]) + TextEditor.didUpdateStyles() + await component.getNextUpdatePromise() + verifyCursorPosition(component, element.querySelector('.cursor'), 2, 2) + }) + it('places the hidden input element at the location of the last cursor if it is visible', async () => { const {component, element, editor} = buildComponent({height: 60, width: 120, rowsPerTile: 2}) const {hiddenInput} = component.refs.cursorsAndInput.refs diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 7bfa9efd7..9d9db7bec 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -549,7 +549,6 @@ class TextEditorComponent { style.willChange = 'transform' style.transform = `translate(${-this.getScrollLeft()}px, ${-this.getScrollTop()}px)` children = [ - this.renderCursorsAndInput(), this.renderLineTiles(), this.renderBlockDecorationMeasurementArea(), this.renderCharacterMeasurementLine(), @@ -557,7 +556,7 @@ class TextEditorComponent { ] } else { children = [ - this.renderCursorsAndInput(), + this.renderLineTiles(), this.renderBlockDecorationMeasurementArea(), this.renderCharacterMeasurementLine() ] @@ -574,69 +573,72 @@ class TextEditorComponent { } renderLineTiles () { - const {lineNodesByScreenLineId, textNodesByScreenLineId} = this - - const startRow = this.getRenderedStartRow() - const endRow = this.getRenderedEndRow() - const rowsPerTile = this.getRowsPerTile() - const tileWidth = this.getScrollWidth() - - const displayLayer = this.props.model.displayLayer - const tileNodes = new Array(this.renderedTileStartRows.length) - - for (let i = 0; i < this.renderedTileStartRows.length; i++) { - const tileStartRow = this.renderedTileStartRows[i] - const tileEndRow = Math.min(endRow, tileStartRow + rowsPerTile) - const tileHeight = this.pixelPositionBeforeBlocksForRow(tileEndRow) - this.pixelPositionBeforeBlocksForRow(tileStartRow) - - tileNodes[i] = $(LinesTileComponent, { - key: this.idsByTileStartRow.get(tileStartRow), - measuredContent: this.measuredContent, - height: tileHeight, - width: tileWidth, - top: this.pixelPositionBeforeBlocksForRow(tileStartRow), - lineHeight: this.getLineHeight(), - renderedStartRow: startRow, - tileStartRow, - tileEndRow, - screenLines: this.renderedScreenLines.slice(tileStartRow - startRow, tileEndRow - startRow), - lineDecorations: this.decorationsToRender.lines.slice(tileStartRow - startRow, tileEndRow - startRow), - textDecorations: this.decorationsToRender.text.slice(tileStartRow - startRow, tileEndRow - startRow), - blockDecorations: this.decorationsToRender.blocks.get(tileStartRow), - highlightDecorations: this.decorationsToRender.highlights.get(tileStartRow), - displayLayer, - nodePool: this.lineNodesPool, - lineNodesByScreenLineId, - textNodesByScreenLineId - }) + const children = [] + const style = { + position: 'absolute', + contain: 'strict', + overflow: 'hidden' } - this.extraRenderedScreenLines.forEach((screenLine, screenRow) => { - if (screenRow < startRow || screenRow >= endRow) { - tileNodes.push($(LineComponent, { - key: 'extra-' + screenLine.id, - screenLine, - screenRow, - displayLayer, + if (this.hasInitialMeasurements) { + const {lineNodesByScreenLineId, textNodesByScreenLineId} = this + + const startRow = this.getRenderedStartRow() + const endRow = this.getRenderedEndRow() + const rowsPerTile = this.getRowsPerTile() + const tileWidth = this.getScrollWidth() + + for (let i = 0; i < this.renderedTileStartRows.length; i++) { + const tileStartRow = this.renderedTileStartRows[i] + const tileEndRow = Math.min(endRow, tileStartRow + rowsPerTile) + const tileHeight = this.pixelPositionBeforeBlocksForRow(tileEndRow) - this.pixelPositionBeforeBlocksForRow(tileStartRow) + + children.push($(LinesTileComponent, { + key: this.idsByTileStartRow.get(tileStartRow), + measuredContent: this.measuredContent, + height: tileHeight, + width: tileWidth, + top: this.pixelPositionBeforeBlocksForRow(tileStartRow), + lineHeight: this.getLineHeight(), + renderedStartRow: startRow, + tileStartRow, + tileEndRow, + screenLines: this.renderedScreenLines.slice(tileStartRow - startRow, tileEndRow - startRow), + lineDecorations: this.decorationsToRender.lines.slice(tileStartRow - startRow, tileEndRow - startRow), + textDecorations: this.decorationsToRender.text.slice(tileStartRow - startRow, tileEndRow - startRow), + blockDecorations: this.decorationsToRender.blocks.get(tileStartRow), + highlightDecorations: this.decorationsToRender.highlights.get(tileStartRow), + displayLayer: this.props.model.displayLayer, nodePool: this.lineNodesPool, lineNodesByScreenLineId, textNodesByScreenLineId })) } - }) - return $.div({ - key: 'lineTiles', - ref: 'lineTiles', - className: 'lines', - style: { - position: 'absolute', - contain: 'strict', - overflow: 'hidden', - width: this.getScrollWidth() + 'px', - height: this.getScrollHeight() + 'px' - } - }, tileNodes) + this.extraRenderedScreenLines.forEach((screenLine, screenRow) => { + if (screenRow < startRow || screenRow >= endRow) { + children.push($(LineComponent, { + key: 'extra-' + screenLine.id, + screenLine, + screenRow, + displayLayer: this.props.model.displayLayer, + nodePool: this.lineNodesPool, + lineNodesByScreenLineId, + textNodesByScreenLineId + })) + } + }) + + style.width = this.getScrollWidth() + 'px' + style.height = this.getScrollHeight() + 'px' + } + + children.push(this.renderCursorsAndInput()) + + return $.div( + {key: 'lineTiles', ref: 'lineTiles', className: 'lines', style}, + children + ) } renderCursorsAndInput () {