From 054c133ed47672bf57c3d0accf51886b95d2b72b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Apr 2017 18:08:11 +0200 Subject: [PATCH] Remeasure block decorations when editor width changes Signed-off-by: Nathan Sobo --- spec/text-editor-component-spec.js | 68 ++++++++++++++++++++++++++++++ src/text-editor-component.js | 48 +++++++++++++++++---- 2 files changed, 108 insertions(+), 8 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 80269f9c9..9cc66fa20 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1402,6 +1402,74 @@ describe('TextEditorComponent', () => { expect(element.contains(item4)).toBe(false) expect(element.contains(item5)).toBe(false) expect(element.contains(item6)).toBe(false) + + // make decoration before row 0 as wide as the editor, and insert some text into it so that it wraps. + item3.style.height = '' + item3.style.margin = '' + item3.style.width = '' + item3.style.wordWrap = 'break-word' + const contentWidthInCharacters = Math.floor(component.getScrollContainerClientWidth() / component.getBaseCharacterWidth()) + item3.textContent = 'x'.repeat(contentWidthInCharacters * 2) + component.invalidateBlockDecorationDimensions(decoration3) + await component.getNextUpdatePromise() + + // make the editor wider, so that the decoration doesn't wrap anymore. + component.element.style.width = ( + component.getGutterContainerWidth() + + component.getScrollContainerClientWidth() * 2 + + component.getVerticalScrollbarWidth() + ) + 'px' + await component.getNextUpdatePromise() + expect(component.getRenderedStartRow()).toBe(0) + expect(component.getRenderedEndRow()).toBe(6) + expect(component.getScrollHeight()).toBe( + editor.getScreenLineCount() * component.getLineHeight() + + getElementHeight(item2) + getElementHeight(item3) + + getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + ) + assertTilesAreSizedAndPositionedCorrectly(component, [ + {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, + {tileStartRow: 3, height: 3 * component.getLineHeight()} + ]) + assertLinesAreAlignedWithLineNumbers(component) + expect(element.querySelectorAll('.line').length).toBe(6) + expect(element.contains(item1)).toBe(false) + expect(item2.previousSibling).toBe(lineNodeForScreenRow(component, 0)) + expect(item2.nextSibling).toBe(lineNodeForScreenRow(component, 1)) + expect(item3.previousSibling).toBeNull() + expect(item3.nextSibling).toBe(lineNodeForScreenRow(component, 0)) + expect(element.contains(item4)).toBe(false) + expect(element.contains(item5)).toBe(false) + expect(element.contains(item6)).toBe(false) + + // make the editor taller and wider and the same time, ensuring the number + // of rendered lines is correct. + setEditorHeightInLines(component, 10) + await setEditorWidthInCharacters(component, 50) + expect(component.getRenderedStartRow()).toBe(0) + expect(component.getRenderedEndRow()).toBe(9) + expect(component.getScrollHeight()).toBe( + editor.getScreenLineCount() * component.getLineHeight() + + getElementHeight(item2) + getElementHeight(item3) + + getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + ) + assertTilesAreSizedAndPositionedCorrectly(component, [ + {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, + {tileStartRow: 3, height: 3 * component.getLineHeight()}, + {tileStartRow: 6, height: 3 * component.getLineHeight() + getElementHeight(item4) + getElementHeight(item5)}, + ]) + assertLinesAreAlignedWithLineNumbers(component) + expect(element.querySelectorAll('.line').length).toBe(9) + expect(element.contains(item1)).toBe(false) + expect(item2.previousSibling).toBe(lineNodeForScreenRow(component, 0)) + expect(item2.nextSibling).toBe(lineNodeForScreenRow(component, 1)) + expect(item3.previousSibling).toBeNull() + expect(item3.nextSibling).toBe(lineNodeForScreenRow(component, 0)) + expect(item4.previousSibling).toBe(lineNodeForScreenRow(component, 6)) + expect(item4.nextSibling).toBe(lineNodeForScreenRow(component, 7)) + expect(item5.previousSibling).toBe(lineNodeForScreenRow(component, 7)) + expect(item5.nextSibling).toBe(lineNodeForScreenRow(component, 8)) + expect(element.contains(item6)).toBe(false) }) function createBlockDecorationAtScreenRow(editor, screenRow, {height, margin, position}) { diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 1915f93be..dbfb9f198 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -182,6 +182,24 @@ class TextEditorComponent { } measureBlockDecorations () { + if (this.remeasureAllBlockDecorations) { + this.remeasureAllBlockDecorations = false + + const decorations = this.props.model.getDecorations() + for (var i = 0; i < decorations.length; i++) { + const decoration = decorations[i] + if (decoration.getProperties().type === 'block') { + this.blockDecorationsToMeasure.add(decoration) + } + } + + // Update the width of the line tiles to ensure block decorations are + // measured with the most recent width. + if (this.blockDecorationsToMeasure.size > 0) { + this.updateSyncBeforeMeasuringContent() + } + } + if (this.blockDecorationsToMeasure.size > 0) { const {blockDecorationMeasurementArea} = this.refs const sentinelElements = new Set() @@ -1188,7 +1206,13 @@ class TextEditorComponent { } didResize () { - if (this.measureClientContainerDimensions()) { + const clientContainerWidthChanged = this.measureClientContainerWidth() + const clientContainerHeightChanged = this.measureClientContainerHeight() + if (clientContainerWidthChanged || clientContainerHeightChanged) { + if (clientContainerWidthChanged) { + this.remeasureAllBlockDecorations = true + } + this.scheduleUpdate() } } @@ -1646,7 +1670,8 @@ class TextEditorComponent { this.measurements = {} this.measureCharacterDimensions() this.measureGutterDimensions() - this.measureClientContainerDimensions() + this.measureClientContainerHeight() + this.measureClientContainerWidth() this.measureScrollbarDimensions() } @@ -1692,22 +1717,29 @@ class TextEditorComponent { return dimensionsChanged } - measureClientContainerDimensions () { + measureClientContainerHeight () { if (!this.measurements) return false - let dimensionsChanged = false const clientContainerHeight = this.refs.clientContainer.offsetHeight - const clientContainerWidth = this.refs.clientContainer.offsetWidth if (clientContainerHeight !== this.measurements.clientContainerHeight) { this.measurements.clientContainerHeight = clientContainerHeight - dimensionsChanged = true + return true + } else { + return false } + } + + measureClientContainerWidth () { + if (!this.measurements) return false + + const clientContainerWidth = this.refs.clientContainer.offsetWidth if (clientContainerWidth !== this.measurements.clientContainerWidth) { this.measurements.clientContainerWidth = clientContainerWidth this.props.model.setEditorWidthInChars(this.getScrollContainerWidth() / this.getBaseCharacterWidth()) - dimensionsChanged = true + return true + } else { + return false } - return dimensionsChanged } measureScrollbarDimensions () {