diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 6bc8cb363..e96d5ecd9 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -37,7 +37,7 @@ describe('TextEditorComponent', () => { expect(element.querySelectorAll('.line-number').length).toBe(9) expect(element.querySelectorAll('.line').length).toBe(9) - component.refs.scroller.scrollTop = 5 * component.measurements.lineHeight + component.setScrollTop(5 * component.getLineHeight()) await component.getNextUpdatePromise() // After scrolling down beyond > 3 rows, the order of line numbers and lines @@ -58,7 +58,7 @@ describe('TextEditorComponent', () => { editor.lineTextForScreenRow(8) ]) - component.refs.scroller.scrollTop = 2.5 * component.measurements.lineHeight + component.setScrollTop(2.5 * component.getLineHeight()) await component.getNextUpdatePromise() expect(Array.from(element.querySelectorAll('.line-number')).map(element => element.textContent.trim())).toEqual([ '1', '2', '3', '4', '5', '6', '7', '8', '9' @@ -88,25 +88,24 @@ describe('TextEditorComponent', () => { it('honors the scrollPastEnd option by adding empty space equivalent to the clientHeight to the end of the content area', async () => { const {component, element, editor} = buildComponent({autoHeight: false, autoWidth: false}) - const {scroller} = component.refs + const {scrollContainer} = component.refs await editor.update({scrollPastEnd: true}) await setEditorHeightInLines(component, 6) // scroll to end - scroller.scrollTop = scroller.scrollHeight - scroller.clientHeight + component.setScrollTop(scrollContainer.scrollHeight - scrollContainer.clientHeight) await component.getNextUpdatePromise() expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() - 3) editor.update({scrollPastEnd: false}) await component.getNextUpdatePromise() // wait for scrollable content resize - await component.getNextUpdatePromise() // wait for async scroll event due to scrollbar shrinking expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() - 6) // Always allows at least 3 lines worth of overscroll if the editor is short await setEditorHeightInLines(component, 2) await editor.update({scrollPastEnd: true}) - scroller.scrollTop = scroller.scrollHeight - scroller.clientHeight + component.setScrollTop(scrollContainer.scrollHeight - scrollContainer.clientHeight) await component.getNextUpdatePromise() expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() + 1) }) @@ -125,18 +124,9 @@ describe('TextEditorComponent', () => { expect(gutterElement.firstChild.style.contain).toBe('strict') }) - it('translates the gutter so it is always visible when scrolling to the right', async () => { - const {component, element, editor} = buildComponent({width: 100}) - - expect(component.refs.gutterContainer.style.transform).toBe('translateX(0px)') - component.refs.scroller.scrollLeft = 100 - await component.getNextUpdatePromise() - expect(component.refs.gutterContainer.style.transform).toBe('translateX(100px)') - }) - it('renders cursors within the visible row range', async () => { const {component, element, editor} = buildComponent({height: 40, rowsPerTile: 2}) - component.refs.scroller.scrollTop = 100 + component.setScrollTop(100) await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(4) @@ -180,8 +170,8 @@ describe('TextEditorComponent', () => { 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 - component.refs.scroller.scrollTop = 100 - component.refs.scroller.scrollLeft = 40 + component.setScrollTop(100) + component.setScrollLeft(40) await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(4) @@ -205,6 +195,7 @@ describe('TextEditorComponent', () => { jasmine.attachToDOM(element) expect(getBaseCharacterWidth(component)).toBe(55) + console.log('running expectation'); expect(lineNodeForScreenRow(component, 3).textContent).toBe( ' var pivot = items.shift(), current, left = [], ' ) @@ -220,8 +211,8 @@ describe('TextEditorComponent', () => { ' = [], right = [];' ) - const {scroller} = component.refs - expect(scroller.clientWidth).toBe(scroller.scrollWidth) + const {scrollContainer} = component.refs + expect(scrollContainer.clientWidth).toBe(scrollContainer.scrollWidth) }) it('decorates the line numbers of folded lines', async () => { @@ -231,29 +222,30 @@ describe('TextEditorComponent', () => { expect(lineNumberNodeForScreenRow(component, 1).classList.contains('folded')).toBe(true) }) - it('makes lines at least as wide as the scroller', async () => { + it('makes lines at least as wide as the scrollContainer', async () => { const {component, element, editor} = buildComponent() - const {scroller, gutterContainer} = component.refs + const {scrollContainer, gutterContainer} = component.refs editor.setText('a') await component.getNextUpdatePromise() - expect(element.querySelector('.line').offsetWidth).toBe(scroller.offsetWidth - gutterContainer.offsetWidth) + expect(element.querySelector('.line').offsetWidth).toBe(scrollContainer.offsetWidth) }) it('resizes based on the content when the autoHeight and/or autoWidth options are true', async () => { const {component, element, editor} = buildComponent({autoHeight: true, autoWidth: true}) + const {gutterContainer, scrollContainer} = component.refs const initialWidth = element.offsetWidth const initialHeight = element.offsetHeight - expect(initialWidth).toBe(component.refs.scroller.scrollWidth) - expect(initialHeight).toBe(component.refs.scroller.scrollHeight) + expect(initialWidth).toBe(gutterContainer.offsetWidth + scrollContainer.scrollWidth) + expect(initialHeight).toBe(scrollContainer.scrollHeight) editor.setCursorScreenPosition([6, Infinity]) editor.insertText('x'.repeat(50)) await component.getNextUpdatePromise() - expect(element.offsetWidth).toBe(component.refs.scroller.scrollWidth) + expect(element.offsetWidth).toBe(gutterContainer.offsetWidth + scrollContainer.scrollWidth) expect(element.offsetWidth).toBeGreaterThan(initialWidth) editor.insertText('\n'.repeat(5)) await component.getNextUpdatePromise() - expect(element.offsetHeight).toBe(component.refs.scroller.scrollHeight) + expect(element.offsetHeight).toBe(scrollContainer.scrollHeight) expect(element.offsetHeight).toBeGreaterThan(initialHeight) }) @@ -369,32 +361,29 @@ describe('TextEditorComponent', () => { describe('autoscroll on cursor movement', () => { it('automatically scrolls vertically when the requested range is within the vertical scroll margin of the top or bottom', async () => { - const {component, element, editor} = buildComponent({height: 120}) - const {scroller} = component.refs + const {component, editor} = buildComponent({height: 120}) expect(component.getLastVisibleRow()).toBe(8) editor.scrollToScreenRange([[4, 0], [6, 0]]) await component.getNextUpdatePromise() - let scrollBottom = scroller.scrollTop + scroller.clientHeight - expect(scrollBottom).toBe((6 + 1 + editor.verticalScrollMargin) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe((6 + 1 + editor.verticalScrollMargin) * component.getLineHeight()) editor.scrollToScreenPosition([8, 0]) await component.getNextUpdatePromise() - scrollBottom = scroller.scrollTop + scroller.clientHeight - expect(scrollBottom).toBe((8 + 1 + editor.verticalScrollMargin) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe((8 + 1 + editor.verticalScrollMargin) * component.measurements.lineHeight) editor.scrollToScreenPosition([3, 0]) await component.getNextUpdatePromise() - expect(scroller.scrollTop).toBe((3 - editor.verticalScrollMargin) * component.measurements.lineHeight) + expect(component.getScrollTop()).toBe((3 - editor.verticalScrollMargin) * component.measurements.lineHeight) editor.scrollToScreenPosition([2, 0]) await component.getNextUpdatePromise() - expect(scroller.scrollTop).toBe(0) + expect(component.getScrollTop()).toBe(0) }) it('does not vertically autoscroll by more than half of the visible lines if the editor is shorter than twice the scroll margin', async () => { const {component, element, editor} = buildComponent({autoHeight: false}) - const {scroller} = component.refs + const {scrollContainer} = component.refs element.style.height = 5.5 * component.measurements.lineHeight + 'px' await component.getNextUpdatePromise() expect(component.getLastVisibleRow()).toBe(6) @@ -402,26 +391,24 @@ describe('TextEditorComponent', () => { editor.scrollToScreenPosition([6, 0]) await component.getNextUpdatePromise() - let scrollBottom = scroller.scrollTop + scroller.clientHeight - expect(scrollBottom).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) editor.scrollToScreenPosition([6, 4]) await component.getNextUpdatePromise() - scrollBottom = scroller.scrollTop + scroller.clientHeight - expect(scrollBottom).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) editor.scrollToScreenRange([[4, 4], [6, 4]]) await component.getNextUpdatePromise() - expect(scroller.scrollTop).toBe((4 - scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollTop()).toBe((4 - scrollMarginInLines) * component.measurements.lineHeight) editor.scrollToScreenRange([[4, 4], [6, 4]], {reversed: false}) await component.getNextUpdatePromise() - expect(scrollBottom).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) }) - it('automatically scrolls horizontally when the requested range is within the horizontal scroll margin of the right edge of the gutter or right edge of the screen', async () => { + it('automatically scrolls horizontally when the requested range is within the horizontal scroll margin of the right edge of the gutter or right edge of the scroll container', async () => { const {component, element, editor} = buildComponent() - const {scroller} = component.refs + const {scrollContainer} = component.refs element.style.width = component.getGutterContainerWidth() + 3 * editor.horizontalScrollMargin * component.measurements.baseCharacterWidth + 'px' @@ -429,32 +416,30 @@ describe('TextEditorComponent', () => { editor.scrollToScreenRange([[1, 12], [2, 28]]) await component.getNextUpdatePromise() - let expectedScrollLeft = Math.floor( + let expectedScrollLeft = Math.round( clientLeftForCharacter(component, 1, 12) - lineNodeForScreenRow(component, 1).getBoundingClientRect().left - (editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) ) - expect(scroller.scrollLeft).toBe(expectedScrollLeft) + expect(component.getScrollLeft()).toBe(expectedScrollLeft) editor.scrollToScreenRange([[1, 12], [2, 28]], {reversed: false}) await component.getNextUpdatePromise() - expectedScrollLeft = Math.floor( + expectedScrollLeft = Math.round( component.getGutterContainerWidth() + clientLeftForCharacter(component, 2, 28) - lineNodeForScreenRow(component, 2).getBoundingClientRect().left + (editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) - - scroller.clientWidth + scrollContainer.clientWidth ) - expect(scroller.scrollLeft).toBe(expectedScrollLeft) + expect(component.getScrollLeft()).toBe(expectedScrollLeft) }) it('does not horizontally autoscroll by more than half of the visible "base-width" characters if the editor is narrower than twice the scroll margin', async () => { - const {component, element, editor} = buildComponent({autoHeight: false}) - const {scroller, gutterContainer} = component.refs + const {component, editor} = buildComponent({autoHeight: false}) await setEditorWidthInCharacters(component, 1.5 * editor.horizontalScrollMargin) - const contentWidth = scroller.clientWidth - gutterContainer.offsetWidth - const contentWidthInCharacters = Math.floor(contentWidth / component.measurements.baseCharacterWidth) + const contentWidthInCharacters = Math.floor(component.getScrollContainerClientWidth() / component.getBaseCharacterWidth()) expect(contentWidthInCharacters).toBe(9) editor.scrollToScreenRange([[6, 10], [6, 15]]) @@ -462,27 +447,26 @@ describe('TextEditorComponent', () => { let expectedScrollLeft = Math.floor( clientLeftForCharacter(component, 6, 10) - lineNodeForScreenRow(component, 1).getBoundingClientRect().left - - (4 * component.measurements.baseCharacterWidth) + (4 * component.getBaseCharacterWidth()) ) - expect(scroller.scrollLeft).toBe(expectedScrollLeft) + expect(component.getScrollLeft()).toBe(expectedScrollLeft) }) it('correctly autoscrolls after inserting a line that exceeds the current content width', async () => { const {component, element, editor} = buildComponent() - const {scroller} = component.refs - element.style.width = component.getGutterContainerWidth() + component.measurements.longestLineWidth + 'px' + element.style.width = component.getGutterContainerWidth() + component.getContentWidth() + 'px' await component.getNextUpdatePromise() editor.setCursorScreenPosition([0, Infinity]) editor.insertText('x'.repeat(100)) await component.getNextUpdatePromise() - expect(scroller.scrollLeft).toBe(component.getScrollWidth() - scroller.clientWidth) + expect(component.getScrollLeft()).toBe(component.getScrollWidth() - component.getScrollContainerClientWidth()) }) it('accounts for the presence of horizontal scrollbars that appear during the same frame as the autoscroll', async () => { const {component, element, editor} = buildComponent() - const {scroller} = component.refs + const {scrollContainer} = component.refs element.style.height = component.getScrollHeight() + 'px' element.style.width = component.getScrollWidth() + 'px' await component.getNextUpdatePromise() @@ -491,8 +475,8 @@ describe('TextEditorComponent', () => { editor.insertText('\n\n' + 'x'.repeat(100)) await component.getNextUpdatePromise() - expect(scroller.scrollTop).toBe(component.getScrollHeight() - scroller.clientHeight) - expect(scroller.scrollLeft).toBe(component.getScrollWidth() - scroller.clientWidth) + expect(component.getScrollTop()).toBe(component.getScrollHeight() - component.getScrollContainerClientHeight()) + expect(component.getScrollLeft()).toBe(component.getScrollWidth() - component.getScrollContainerClientWidth()) }) }) @@ -735,7 +719,7 @@ describe('TextEditorComponent', () => { ) // Don't flash on next update if another flash wasn't requested - component.refs.scroller.scrollTop = 100 + component.setScrollTop(100) await component.getNextUpdatePromise() expect(highlights[0].classList.contains('b')).toBe(false) expect(highlights[1].classList.contains('b')).toBe(false) @@ -1156,29 +1140,29 @@ describe('TextEditorComponent', () => { expect(editor.getCursorScreenPosition()).toEqual([0, 0]) }) - it('autoscrolls the content when dragging near the edge of the screen', async () => { - const {component, editor} = buildComponent({width: 200, height: 200}) - const {scroller} = component.refs + it('autoscrolls the content when dragging near the edge of the scroll container', async () => { + const {component, element, editor} = buildComponent({width: 200, height: 200}) spyOn(component, 'handleMouseDragUntilMouseUp') let previousScrollTop = 0 let previousScrollLeft = 0 function assertScrolledDownAndRight () { - expect(scroller.scrollTop).toBeGreaterThan(previousScrollTop) - previousScrollTop = scroller.scrollTop - expect(scroller.scrollLeft).toBeGreaterThan(previousScrollLeft) - previousScrollLeft = scroller.scrollLeft + expect(component.getScrollTop()).toBeGreaterThan(previousScrollTop) + previousScrollTop = component.getScrollTop() + expect(component.getScrollLeft()).toBeGreaterThan(previousScrollLeft) + previousScrollLeft = component.getScrollLeft() } function assertScrolledUpAndLeft () { - expect(scroller.scrollTop).toBeLessThan(previousScrollTop) - previousScrollTop = scroller.scrollTop - expect(scroller.scrollLeft).toBeLessThan(previousScrollLeft) - previousScrollLeft = scroller.scrollLeft + expect(component.getScrollTop()).toBeLessThan(previousScrollTop) + previousScrollTop = component.getScrollTop() + expect(component.getScrollLeft()).toBeLessThan(previousScrollLeft) + previousScrollLeft = component.getScrollLeft() } component.didMouseDownOnContent({detail: 1, button: 0, clientX: 100, clientY: 100}) const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[0][0] + didDrag({clientX: 199, clientY: 199}) assertScrolledDownAndRight() didDrag({clientX: 199, clientY: 199}) @@ -1192,27 +1176,24 @@ describe('TextEditorComponent', () => { didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) assertScrolledUpAndLeft() - // Don't artificially update scroll measurements beyond the minimum or - // maximum possible scroll positions - expect(scroller.scrollTop).toBe(0) - expect(scroller.scrollLeft).toBe(0) + // Don't artificially update scroll position beyond possible values + expect(component.getScrollTop()).toBe(0) + expect(component.getScrollLeft()).toBe(0) didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) - expect(component.measurements.scrollTop).toBe(0) - expect(scroller.scrollTop).toBe(0) - expect(component.measurements.scrollLeft).toBe(0) - expect(scroller.scrollLeft).toBe(0) + expect(component.getScrollTop()).toBe(0) + expect(component.getScrollLeft()).toBe(0) - const maxScrollTop = scroller.scrollHeight - scroller.clientHeight - const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth - scroller.scrollTop = maxScrollTop - scroller.scrollLeft = maxScrollLeft + const maxScrollTop = component.getMaxScrollTop() + const maxScrollLeft = component.getMaxScrollLeft() + component.setScrollTop(maxScrollTop) + component.setScrollLeft(maxScrollLeft) await component.getNextUpdatePromise() didDrag({clientX: 199, clientY: 199}) didDrag({clientX: 199, clientY: 199}) didDrag({clientX: 199, clientY: 199}) - expect(component.measurements.scrollTop).toBe(maxScrollTop) - expect(component.measurements.scrollLeft).toBe(maxScrollLeft) + expect(component.getScrollTop()).toBe(maxScrollTop) + expect(component.getScrollLeft()).toBe(maxScrollLeft) }) }) @@ -1387,25 +1368,25 @@ describe('TextEditorComponent', () => { expect(editor.isFoldedAtScreenRow(1)).toBe(false) }) - it('autoscrolls the content when dragging near the edge of the screen', async () => { + it('autoscrolls when dragging near the top or bottom of the gutter', async () => { const {component, editor} = buildComponent({width: 200, height: 200}) - const {scroller} = component.refs + const {scrollContainer} = component.refs spyOn(component, 'handleMouseDragUntilMouseUp') let previousScrollTop = 0 let previousScrollLeft = 0 function assertScrolledDown () { - expect(scroller.scrollTop).toBeGreaterThan(previousScrollTop) - previousScrollTop = scroller.scrollTop - expect(scroller.scrollLeft).toBe(previousScrollLeft) - previousScrollLeft = scroller.scrollLeft + expect(component.getScrollTop()).toBeGreaterThan(previousScrollTop) + previousScrollTop = component.getScrollTop() + expect(component.getScrollLeft()).toBe(previousScrollLeft) + previousScrollLeft = component.getScrollLeft() } function assertScrolledUp () { - expect(scroller.scrollTop).toBeLessThan(previousScrollTop) - previousScrollTop = scroller.scrollTop - expect(scroller.scrollLeft).toBe(previousScrollLeft) - previousScrollLeft = scroller.scrollLeft + expect(component.getScrollTop()).toBeLessThan(previousScrollTop) + previousScrollTop = component.getScrollTop() + expect(component.getScrollLeft()).toBe(previousScrollLeft) + previousScrollLeft = component.getScrollLeft() } component.didMouseDownOnLineNumberGutter({detail: 1, button: 0, clientX: 0, clientY: 100}) @@ -1425,25 +1406,23 @@ describe('TextEditorComponent', () => { // Don't artificially update scroll measurements beyond the minimum or // maximum possible scroll positions - expect(scroller.scrollTop).toBe(0) - expect(scroller.scrollLeft).toBe(0) + expect(component.getScrollTop()).toBe(0) + expect(component.getScrollLeft()).toBe(0) didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) - expect(component.measurements.scrollTop).toBe(0) - expect(scroller.scrollTop).toBe(0) - expect(component.measurements.scrollLeft).toBe(0) - expect(scroller.scrollLeft).toBe(0) + expect(component.getScrollTop()).toBe(0) + expect(component.getScrollLeft()).toBe(0) - const maxScrollTop = scroller.scrollHeight - scroller.clientHeight - const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth - scroller.scrollTop = maxScrollTop - scroller.scrollLeft = maxScrollLeft + const maxScrollTop = component.getMaxScrollTop() + const maxScrollLeft = component.getMaxScrollLeft() + component.setScrollTop(maxScrollTop) + component.setScrollLeft(maxScrollLeft) await component.getNextUpdatePromise() didDrag({clientX: 199, clientY: 199}) didDrag({clientX: 199, clientY: 199}) didDrag({clientX: 199, clientY: 199}) - expect(component.measurements.scrollTop).toBe(maxScrollTop) - expect(component.measurements.scrollLeft).toBe(maxScrollLeft) + expect(component.getScrollTop()).toBe(maxScrollTop) + expect(component.getScrollLeft()).toBe(maxScrollLeft) }) }) }) @@ -1475,10 +1454,7 @@ function buildComponent (params = {}) { } function getBaseCharacterWidth (component) { - return Math.round( - (component.refs.scroller.clientWidth - component.getGutterContainerWidth()) / - component.measurements.baseCharacterWidth - ) + return Math.round(component.getScrollContainerWidth() / component.getBaseCharacterWidth()) } async function setEditorHeightInLines(component, heightInLines) { diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 3b09ba7d1..1016dcb7c 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -16,6 +16,7 @@ const KOREAN_CHARACTER = '세' const NBSP_CHARACTER = '\u00a0' const ZERO_WIDTH_NBSP_CHARACTER = '\ufeff' const MOUSE_DRAG_AUTOSCROLL_MARGIN = 40 +const MOUSE_WHEEL_SCROLL_SENSITIVITY = 0.8 function scaleMouseDragAutoscrollDelta (delta) { return Math.pow(delta / 3, 3) / 280 @@ -47,7 +48,8 @@ class TextEditorComponent { this.lineNodesByScreenLineId = new Map() this.textNodesByScreenLineId = new Map() this.pendingAutoscroll = null - this.autoscrollTop = null + this.scrollTop = 0 + this.scrollLeft = 0 this.previousScrollWidth = 0 this.previousScrollHeight = 0 this.lastKeydown = null @@ -97,7 +99,7 @@ class TextEditorComponent { } this.horizontalPositionsToMeasure.clear() - if (this.pendingAutoscroll) this.initiateAutoscroll() + if (this.pendingAutoscroll) this.autoscrollVertically() this.populateVisibleRowRange() const longestLineToMeasure = this.checkForNewLongestLine() this.queryScreenLinesToRender() @@ -111,19 +113,10 @@ class TextEditorComponent { etch.updateSync(this) - // If scrollHeight or scrollWidth changed, we may have shown or hidden - // scrollbars, affecting the clientWidth or clientHeight - if (this.checkIfScrollDimensionsChanged()) { - this.measureClientDimensions() - // If the clientHeight changed, our previous vertical autoscroll may have - // been off by the height of the horizontal scrollbar. If we *still* need - // to autoscroll, just re-render the frame. - if (this.pendingAutoscroll && this.initiateAutoscroll()) { - this.updateSync() - return - } + if (this.pendingAutoscroll) { + this.autoscrollHorizontally() + this.pendingAutoscroll = null } - if (this.pendingAutoscroll) this.finalizeAutoscroll() this.currentFrameLineNumberGutterProps = null } @@ -142,103 +135,82 @@ class TextEditorComponent { render () { const {model} = this.props - const style = { - overflow: 'hidden', - } + const style = {} if (!model.getAutoHeight() && !model.getAutoWidth()) { style.contain = 'strict' } - if (this.measurements) { if (model.getAutoHeight()) { - style.height = this.getScrollHeight() + 'px' + style.height = this.getContentHeight() + 'px' } if (model.getAutoWidth()) { - style.width = this.getScrollWidth() + 'px' + style.width = this.getGutterContainerWidth() + this.getContentWidth() + 'px' } } let attributes = null let className = 'editor' - if (this.focused) { - className += ' is-focused' - } + if (this.focused) className += ' is-focused' if (model.isMini()) { attributes = {mini: ''} className += ' mini' } - const scrollerOverflowX = (model.isMini() || model.isSoftWrapped()) ? 'hidden' : 'auto' - const scrollerOverflowY = model.isMini() ? 'hidden' : 'auto' - return $('atom-text-editor', { className, - attributes, style, + attributes, tabIndex: -1, on: { focus: this.didFocus, - blur: this.didBlur + blur: this.didBlur, + mousewheel: this.didMouseWheel } }, $.div( { + ref: 'clientContainer', style: { position: 'relative', + contain: 'strict', + overflow: 'hidden', + backgroundColor: 'inherit', width: '100%', - height: '100%', - backgroundColor: 'inherit' + height: '100%' } }, - $.div( - { - ref: 'scroller', - className: 'scroll-view', - on: {scroll: this.didScroll}, - style: { - position: 'absolute', - contain: 'strict', - top: 0, - right: 0, - bottom: 0, - left: 0, - overflowX: scrollerOverflowX, - overflowY: scrollerOverflowY, - backgroundColor: 'inherit' - } - }, - $.div( - { - style: { - isolate: 'content', - width: 'max-content', - height: 'max-content', - backgroundColor: 'inherit' - } - }, - this.renderGutterContainer(), - this.renderContent() - ) - ) + this.renderGutterContainer(), + this.renderScrollContainer() ) ) } renderGutterContainer () { if (this.props.model.isMini()) return null - const props = {ref: 'gutterContainer', className: 'gutter-container'} + const innerStyle = { + willChange: 'transform', + backgroundColor: 'inherit' + } if (this.measurements) { - props.style = { - position: 'relative', - willChange: 'transform', - transform: `translateX(${this.measurements.scrollLeft}px)`, - zIndex: 1 - } + innerStyle.transform = `translateY(${-this.getScrollTop()}px)` } - return $.div(props, this.renderLineNumberGutter()) + return $.div( + { + ref: 'gutterContainer', + className: 'gutter-container', + style: { + position: 'relative', + zIndex: 1, + backgroundColor: 'inherit' + } + }, + $.div({style: innerStyle}, + this.renderLineNumberGutter() + ) + ) } renderLineNumberGutter () { @@ -278,8 +250,8 @@ class TextEditorComponent { ref: 'lineNumberGutter', parentComponent: this, height: this.getScrollHeight(), - width: this.measurements.lineNumberGutterWidth, - lineHeight: this.measurements.lineHeight, + width: this.getLineNumberGutterWidth(), + lineHeight: this.getLineHeight(), startRow, endRow, rowsPerTile, maxLineNumberDigits, bufferRows, lineNumberDecorations, softWrappedFlags, foldableFlags @@ -301,6 +273,31 @@ class TextEditorComponent { } } + renderScrollContainer () { + const style = { + position: 'absolute', + contain: 'strict', + overflow: 'hidden', + top: 0, + bottom: 0, + backgroundColor: 'inherit' + } + + if (this.measurements) { + style.left = this.getGutterContainerWidth() + 'px' + style.width = this.getScrollContainerWidth() + 'px' + } + + return $.div( + { + ref: 'scrollContainer', + className: 'scroll-view', + style + }, + this.renderContent() + ) + } + renderContent () { let children let style = { @@ -309,15 +306,13 @@ class TextEditorComponent { backgroundColor: 'inherit' } if (this.measurements) { - const contentWidth = this.getContentWidth() - const scrollHeight = this.getScrollHeight() - const width = contentWidth + 'px' - const height = scrollHeight + 'px' - style.width = width - style.height = height + style.width = this.getScrollWidth() + 'px' + style.height = this.getScrollHeight() + 'px' + style.willChange = 'transform' + style.transform = `translate(${-this.getScrollLeft()}px, ${-this.getScrollTop()}px)` children = [ - this.renderCursorsAndInput(width, height), - this.renderLineTiles(width, height), + this.renderCursorsAndInput(), + this.renderLineTiles(), this.renderPlaceholderText() ] } else { @@ -339,7 +334,7 @@ class TextEditorComponent { ) } - renderLineTiles (width, height) { + renderLineTiles () { if (!this.measurements) return [] const {lineNodesByScreenLineId, textNodesByScreenLineId} = this @@ -347,8 +342,8 @@ class TextEditorComponent { const startRow = this.getRenderedStartRow() const endRow = this.getRenderedEndRow() const rowsPerTile = this.getRowsPerTile() - const tileHeight = this.measurements.lineHeight * rowsPerTile - const tileWidth = this.getContentWidth() + const tileHeight = this.getLineHeight() * rowsPerTile + const tileWidth = this.getScrollWidth() const displayLayer = this.props.model.displayLayer const tileNodes = new Array(this.getRenderedTileCount()) @@ -368,7 +363,7 @@ class TextEditorComponent { height: tileHeight, width: tileWidth, top: this.topPixelPositionForRow(tileStartRow), - lineHeight: this.measurements.lineHeight, + lineHeight: this.getLineHeight(), screenLines: this.renderedScreenLines.slice(tileStartRow - startRow, tileEndRow - startRow), lineDecorations, highlightDecorations, @@ -395,14 +390,16 @@ class TextEditorComponent { style: { position: 'absolute', contain: 'strict', - width, height, + overflow: 'hidden', + width: this.getScrollWidth() + 'px', + height: this.getScrollHeight() + 'px', backgroundColor: 'inherit' } }, tileNodes) } - renderCursorsAndInput (width, height) { - const cursorHeight = this.measurements.lineHeight + 'px' + renderCursorsAndInput () { + const cursorHeight = this.getLineHeight() + 'px' const children = [this.renderHiddenInput()] @@ -425,7 +422,8 @@ class TextEditorComponent { position: 'absolute', contain: 'strict', zIndex: 1, - width, height + width: this.getScrollWidth() + 'px', + height: this.getScrollHeight() + 'px' } }, children) } @@ -470,7 +468,7 @@ class TextEditorComponent { style: { position: 'absolute', width: '1px', - height: this.measurements.lineHeight + 'px', + height: this.getLineHeight() + 'px', top: top + 'px', left: left + 'px', opacity: 0, @@ -646,7 +644,7 @@ class TextEditorComponent { updateCursorsToRender () { this.decorationsToRender.cursors.length = 0 - const height = this.measurements.lineHeight + 'px' + const height = this.getLineHeight() + 'px' for (let i = 0; i < this.decorationsToMeasure.cursors.length; i++) { const cursor = this.decorationsToMeasure.cursors[i] const {row, column} = cursor.screenPosition @@ -765,15 +763,20 @@ class TextEditorComponent { } } - didScroll () { - if (this.measureScrollPosition(true)) { - this.updateSync() - } + didMouseWheel (eveWt) { + let {deltaX, deltaY} = event + deltaX = deltaX * MOUSE_WHEEL_SCROLL_SENSITIVITY + deltaY = deltaY * MOUSE_WHEEL_SCROLL_SENSITIVITY + + const scrollPositionChanged = + this.setScrollLeft(this.getScrollLeft() + deltaX) || + this.setScrollTop(this.getScrollTop() + deltaY) + + if (scrollPositionChanged) this.updateSync() } didResize () { - if (this.measureEditorDimensions()) { - this.measureClientDimensions() + if (this.measureClientContainerDimensions()) { this.scheduleUpdate() } } @@ -1023,10 +1026,10 @@ class TextEditorComponent { } autoscrollOnMouseDrag ({clientX, clientY}, verticalOnly = false) { - let {top, bottom, left, right} = this.refs.scroller.getBoundingClientRect() + let {top, bottom, left, right} = this.refs.scrollContainer.getBoundingClientRect() top += MOUSE_DRAG_AUTOSCROLL_MARGIN bottom -= MOUSE_DRAG_AUTOSCROLL_MARGIN - left += this.getGutterContainerWidth() + MOUSE_DRAG_AUTOSCROLL_MARGIN + left += MOUSE_DRAG_AUTOSCROLL_MARGIN right -= MOUSE_DRAG_AUTOSCROLL_MARGIN let yDelta, yDirection @@ -1050,31 +1053,21 @@ class TextEditorComponent { let scrolled = false if (yDelta != null) { const scaledDelta = scaleMouseDragAutoscrollDelta(yDelta) * yDirection - const newScrollTop = this.constrainScrollTop(this.measurements.scrollTop + scaledDelta) - if (newScrollTop !== this.measurements.scrollTop) { - this.measurements.scrollTop = newScrollTop - this.refs.scroller.scrollTop = newScrollTop - scrolled = true - } + scrolled = this.setScrollTop(this.getScrollTop() + scaledDelta) } if (!verticalOnly && xDelta != null) { const scaledDelta = scaleMouseDragAutoscrollDelta(xDelta) * xDirection - const newScrollLeft = this.constrainScrollLeft(this.measurements.scrollLeft + scaledDelta) - if (newScrollLeft !== this.measurements.scrollLeft) { - this.measurements.scrollLeft = newScrollLeft - this.refs.scroller.scrollLeft = newScrollLeft - scrolled = true - } + scrolled = this.setScrollLeft(this.getScrollLeft() + scaledDelta) } if (scrolled) this.updateSync() } screenPositionForMouseEvent ({clientX, clientY}) { - const scrollerRect = this.refs.scroller.getBoundingClientRect() - clientX = Math.min(scrollerRect.right, Math.max(scrollerRect.left, clientX)) - clientY = Math.min(scrollerRect.bottom, Math.max(scrollerRect.top, clientY)) + const scrollContainerRect = this.refs.scrollContainer.getBoundingClientRect() + clientX = Math.min(scrollContainerRect.right, Math.max(scrollContainerRect.left, clientX)) + clientY = Math.min(scrollContainerRect.bottom, Math.max(scrollContainerRect.top, clientY)) const linesRect = this.refs.lineTiles.getBoundingClientRect() return this.screenPositionForPixelPosition({ top: clientY - linesRect.top, @@ -1087,12 +1080,12 @@ class TextEditorComponent { this.scheduleUpdate() } - initiateAutoscroll () { + autoscrollVertically () { const {screenRange, options} = this.pendingAutoscroll const screenRangeTop = this.pixelTopForRow(screenRange.start.row) - const screenRangeBottom = this.pixelTopForRow(screenRange.end.row) + this.measurements.lineHeight - const verticalScrollMargin = this.getVerticalScrollMargin() + const screenRangeBottom = this.pixelTopForRow(screenRange.end.row) + this.getLineHeight() + const verticalScrollMargin = this.getVerticalAutoscrollMargin() this.requestHorizontalMeasurement(screenRange.start.row, screenRange.start.column) this.requestHorizontalMeasurement(screenRange.end.row, screenRange.end.column) @@ -1109,43 +1102,27 @@ class TextEditorComponent { desiredScrollBottom = screenRangeBottom + verticalScrollMargin } - if (desiredScrollTop != null) { - desiredScrollTop = this.constrainScrollTop(desiredScrollTop) - } - - if (desiredScrollBottom != null) { - desiredScrollBottom = this.constrainScrollTop(desiredScrollBottom - this.getClientHeight()) + this.getClientHeight() - } - if (!options || options.reversed !== false) { if (desiredScrollBottom > this.getScrollBottom()) { - this.autoscrollTop = desiredScrollBottom - this.measurements.clientHeight - this.measurements.scrollTop = this.autoscrollTop - return true + return this.setScrollBottom(desiredScrollBottom, true) } if (desiredScrollTop < this.getScrollTop()) { - this.autoscrollTop = desiredScrollTop - this.measurements.scrollTop = this.autoscrollTop - return true + return this.setScrollTop(desiredScrollTop, true) } } else { if (desiredScrollTop < this.getScrollTop()) { - this.autoscrollTop = desiredScrollTop - this.measurements.scrollTop = this.autoscrollTop - return true + return this.setScrollTop(desiredScrollTop, true) } if (desiredScrollBottom > this.getScrollBottom()) { - this.autoscrollTop = desiredScrollBottom - this.measurements.clientHeight - this.measurements.scrollTop = this.autoscrollTop - return true + return this.setScrollBottom(desiredScrollBottom, true) } } return false } - finalizeAutoscroll () { - const horizontalScrollMargin = this.getHorizontalScrollMargin() + autoscrollHorizontally () { + const horizontalScrollMargin = this.getHorizontalAutoscrollMargin() const {screenRange, options} = this.pendingAutoscroll const gutterContainerWidth = this.getGutterContainerWidth() @@ -1154,121 +1131,70 @@ class TextEditorComponent { const desiredScrollLeft = Math.max(0, left - horizontalScrollMargin - gutterContainerWidth) const desiredScrollRight = Math.min(this.getScrollWidth(), right + horizontalScrollMargin) - let autoscrollLeft if (!options || options.reversed !== false) { if (desiredScrollRight > this.getScrollRight()) { - autoscrollLeft = desiredScrollRight - this.getClientWidth() - this.measurements.scrollLeft = autoscrollLeft + this.setScrollRight(desiredScrollRight, true) } if (desiredScrollLeft < this.getScrollLeft()) { - autoscrollLeft = desiredScrollLeft - this.measurements.scrollLeft = autoscrollLeft + this.setScrollLeft(desiredScrollLeft, true) } } else { if (desiredScrollLeft < this.getScrollLeft()) { - autoscrollLeft = desiredScrollLeft - this.measurements.scrollLeft = autoscrollLeft + this.setScrollLeft(desiredScrollLeft, true) } if (desiredScrollRight > this.getScrollRight()) { - autoscrollLeft = desiredScrollRight - this.getClientWidth() - this.measurements.scrollLeft = autoscrollLeft + this.setScrollRight(desiredScrollRight, true) } } - - if (this.autoscrollTop != null) { - this.refs.scroller.scrollTop = this.autoscrollTop - this.autoscrollTop = null - } - - if (autoscrollLeft != null) { - this.refs.scroller.scrollLeft = autoscrollLeft - } - - this.pendingAutoscroll = null } - getVerticalScrollMargin () { - const {clientHeight, lineHeight} = this.measurements + getVerticalAutoscrollMargin () { + const maxMarginInLines = Math.floor( + (this.getScrollContainerClientHeight() / this.getLineHeight() - 1) / 2 + ) const marginInLines = Math.min( this.props.model.verticalScrollMargin, - Math.floor(((clientHeight / lineHeight) - 1) / 2) + maxMarginInLines ) - return marginInLines * lineHeight + return marginInLines * this.getLineHeight() } - getHorizontalScrollMargin () { - const {clientWidth, baseCharacterWidth} = this.measurements - const contentClientWidth = clientWidth - this.getGutterContainerWidth() + getHorizontalAutoscrollMargin () { + const maxMarginInBaseCharacters = Math.floor( + (this.getScrollContainerClientWidth() / this.getBaseCharacterWidth() - 1) / 2 + ) const marginInBaseCharacters = Math.min( this.props.model.horizontalScrollMargin, - Math.floor(((contentClientWidth / baseCharacterWidth) - 1) / 2) - ) - return marginInBaseCharacters * baseCharacterWidth - } - - constrainScrollTop (desiredScrollTop) { - return Math.max( - 0, Math.min(desiredScrollTop, this.getScrollHeight() - this.getClientHeight()) - ) - } - - constrainScrollLeft (desiredScrollLeft) { - return Math.max( - 0, Math.min(desiredScrollLeft, this.getScrollWidth() - this.getClientWidth()) + maxMarginInBaseCharacters ) + return marginInBaseCharacters * this.getBaseCharacterWidth() } performInitialMeasurements () { this.measurements = {} - this.measureGutterDimensions() - this.measureEditorDimensions() - this.measureClientDimensions() - this.measureScrollPosition() this.measureCharacterDimensions() + this.measureGutterDimensions() + this.measureClientContainerDimensions() } - measureEditorDimensions () { + measureClientContainerDimensions () { if (!this.measurements) return false let dimensionsChanged = false - const scrollerHeight = this.refs.scroller.offsetHeight - const scrollerWidth = this.refs.scroller.offsetWidth - if (scrollerHeight !== this.measurements.scrollerHeight) { - this.measurements.scrollerHeight = scrollerHeight + const clientContainerHeight = this.refs.clientContainer.offsetHeight + const clientContainerWidth = this.refs.clientContainer.offsetWidth + if (clientContainerHeight !== this.measurements.clientContainerHeight) { + this.measurements.clientContainerHeight = clientContainerHeight dimensionsChanged = true } - if (scrollerWidth !== this.measurements.scrollerWidth) { - this.measurements.scrollerWidth = scrollerWidth + if (clientContainerWidth !== this.measurements.clientContainerWidth) { + this.measurements.clientContainerWidth = clientContainerWidth + this.props.model.setEditorWidthInChars(this.getScrollContainerWidth() / this.getBaseCharacterWidth()) dimensionsChanged = true } return dimensionsChanged } - measureScrollPosition () { - let scrollPositionChanged = false - const {scrollTop, scrollLeft} = this.refs.scroller - if (scrollTop !== this.measurements.scrollTop) { - this.measurements.scrollTop = scrollTop - scrollPositionChanged = true - } - if (scrollLeft !== this.measurements.scrollLeft) { - this.measurements.scrollLeft = scrollLeft - scrollPositionChanged = true - } - return scrollPositionChanged - } - - measureClientDimensions () { - const {clientHeight, clientWidth} = this.refs.scroller - if (clientHeight !== this.measurements.clientHeight) { - this.measurements.clientHeight = clientHeight - } - if (clientWidth !== this.measurements.clientWidth) { - this.measurements.clientWidth = clientWidth - this.props.model.setWidth(clientWidth - this.getGutterContainerWidth(), true) - } - } - measureCharacterDimensions () { this.measurements.lineHeight = this.refs.characterMeasurementLine.getBoundingClientRect().height this.measurements.baseCharacterWidth = this.refs.normalWidthCharacterSpan.getBoundingClientRect().width @@ -1383,7 +1309,7 @@ class TextEditorComponent { } pixelTopForRow (row) { - return row * this.measurements.lineHeight + return row * this.getLineHeight() } pixelLeftForRowAndColumn (row, column) { @@ -1478,73 +1404,91 @@ class TextEditorComponent { return this.element.offsetWidth > 0 || this.element.offsetHeight > 0 } + getLineHeight () { + return this.measurements.lineHeight + } + getBaseCharacterWidth () { return this.measurements ? this.measurements.baseCharacterWidth : null } - getScrollTop () { - if (this.measurements != null) { - return this.measurements.scrollTop + getLongestLineWidth () { + return this.measurements.longestLineWidth + } + + getClientContainerHeight () { + return this.measurements.clientContainerHeight + } + + getClientContainerWidth () { + return this.measurements.clientContainerWidth + } + + getScrollContainerWidth () { + if (this.props.model.getAutoWidth()) { + return this.getScrollWidth() + } else { + return this.getClientContainerWidth() - this.getGutterContainerWidth() } } - getScrollBottom () { - return this.measurements - ? this.measurements.scrollTop + this.measurements.clientHeight - : null + getScrollContainerHeight () { + if (this.props.model.getAutoHeight()) { + return this.getScrollHeight() + } else { + return this.getClientContainerHeight() + } } - getScrollLeft () { - return this.measurements ? this.measurements.scrollLeft : null + getScrollContainerHeightInLines () { + return Math.ceil(this.getScrollContainerHeight() / this.getLineHeight()) } - getScrollRight () { - return this.measurements - ? this.measurements.scrollLeft + this.measurements.clientWidth - : null + getScrollContainerClientWidth () { + return this.getScrollContainerWidth() + } + + getScrollContainerClientHeight () { + return this.getScrollContainerHeight() } getScrollHeight () { - const {model} = this.props - const contentHeight = model.getApproximateScreenLineCount() * this.measurements.lineHeight - if (model.getScrollPastEnd()) { - const extraScrollHeight = Math.max( - 3 * this.measurements.lineHeight, - this.getClientHeight() - 3 * this.measurements.lineHeight + if (this.props.model.getScrollPastEnd()) { + return this.getContentHeight() + Math.max( + 3 * this.getLineHeight(), + this.getScrollContainerClientHeight() - (3 * this.getLineHeight()) ) - return contentHeight + extraScrollHeight } else { - return contentHeight + return this.getContentHeight() } } getScrollWidth () { - return this.getContentWidth() + this.getGutterContainerWidth() + const {model} = this.props + + if (model.isSoftWrapped()) { + return this.getScrollContainerClientWidth() + } else if (model.getAutoWidth()) { + return this.getContentWidth() + } else { + return Math.max(this.getContentWidth(), this.getScrollContainerClientWidth()) + } } - getClientHeight () { - return this.measurements.clientHeight - } - - getClientWidth () { - return this.measurements.clientWidth - } - - getGutterContainerWidth () { - return this.measurements.lineNumberGutterWidth + getContentHeight () { + return this.props.model.getApproximateScreenLineCount() * this.getLineHeight() } getContentWidth () { - if (this.props.model.isSoftWrapped()) { - return this.getClientWidth() - this.getGutterContainerWidth() - } else if (this.props.model.getAutoWidth()) { - return Math.round(this.measurements.longestLineWidth + this.measurements.baseCharacterWidth) - } else { - return Math.max( - Math.round(this.measurements.longestLineWidth + this.measurements.baseCharacterWidth), - this.measurements.scrollerWidth - this.getGutterContainerWidth() - ) - } + return Math.round(this.getLongestLineWidth() + this.getBaseCharacterWidth()) + } + + getGutterContainerWidth () { + return this.getLineNumberGutterWidth() + } + + getLineNumberGutterWidth () { + return this.measurements.lineNumberGutterWidth } getRowsPerTile () { @@ -1583,16 +1527,13 @@ class TextEditorComponent { } getFirstVisibleRow () { - const scrollTop = this.getScrollTop() - const lineHeight = this.measurements.lineHeight - return Math.floor(scrollTop / lineHeight) + return Math.floor(this.getScrollTop() / this.getLineHeight()) } getLastVisibleRow () { - const {scrollerHeight, lineHeight} = this.measurements return Math.min( this.props.model.getApproximateScreenLineCount() - 1, - this.getFirstVisibleRow() + Math.ceil(scrollerHeight / lineHeight) + this.getFirstVisibleRow() + this.getScrollContainerHeightInLines() ) } @@ -1600,6 +1541,63 @@ class TextEditorComponent { return Math.floor((this.getLastVisibleRow() - this.getFirstVisibleRow()) / this.getRowsPerTile()) + 2 } + + getScrollTop () { + this.scrollTop = Math.min(this.getMaxScrollTop(), this.scrollTop) + return this.scrollTop + } + + setScrollTop (scrollTop, suppressUpdate = false) { + scrollTop = Math.round(Math.max(0, Math.min(this.getMaxScrollTop(), scrollTop))) + if (scrollTop !== this.scrollTop) { + this.scrollTop = scrollTop + if (!suppressUpdate) this.scheduleUpdate() + return true + } else { + return false + } + } + + getMaxScrollTop () { + return Math.max(0, this.getScrollHeight() - this.getScrollContainerClientHeight()) + } + + getScrollBottom () { + return this.getScrollTop() + this.getScrollContainerClientHeight() + } + + setScrollBottom (scrollBottom, suppressUpdate = false) { + return this.setScrollTop(scrollBottom - this.getScrollContainerClientHeight(), suppressUpdate) + } + + getScrollLeft () { + // this.scrollLeft = Math.min(this.getMaxScrollLeft(), this.scrollLeft) + return this.scrollLeft + } + + setScrollLeft (scrollLeft, suppressUpdate = false) { + scrollLeft = Math.round(Math.max(0, Math.min(this.getMaxScrollLeft(), scrollLeft))) + if (scrollLeft !== this.scrollLeft) { + this.scrollLeft = scrollLeft + if (!suppressUpdate) this.scheduleUpdate() + return true + } else { + return false + } + } + + getMaxScrollLeft () { + return Math.max(0, this.getScrollWidth() - this.getScrollContainerClientWidth()) + } + + getScrollRight () { + return this.getScrollLeft() + this.getScrollContainerClientWidth() + } + + setScrollRight (scrollRight, suppressUpdate = false) { + return this.setScrollLeft(scrollRight - this.getScrollContainerClientWidth(), suppressUpdate) + } + // Ensure the spatial index is populated with rows that are currently // visible so we *at least* get the longest row in the visible range. populateVisibleRowRange () { @@ -1608,7 +1606,7 @@ class TextEditorComponent { } topPixelPositionForRow (row) { - return row * this.measurements.lineHeight + return row * this.getLineHeight() } getNextUpdatePromise () { @@ -1829,7 +1827,7 @@ class LinesTileComponent { position: 'absolute', contain: 'strict', height: height + 'px', - width: width + 'px', + width: width + 'px' } }, children) } diff --git a/src/text-editor-element.js b/src/text-editor-element.js index 38bedfe0e..eb64e5fa7 100644 --- a/src/text-editor-element.js +++ b/src/text-editor-element.js @@ -18,7 +18,7 @@ class TextEditorElement extends HTMLElement { } getModel () { - return this.getComponent().getModel() + return this.getComponent().props.model } setModel (model) {