From 527ada47f9276a9ff94f92d181fea5e25dbee8af Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 24 Apr 2014 19:07:17 -0600 Subject: [PATCH] Account for gutter width in scrollWidth of horizontal scrollbar Because the scrollbar now spans the entire editor but the scrollable area does not include the gutter, we need to add the current width of the gutter to the scroll width of the horizontal scrollbar to allow it to scroll to the end of the longest lines. --- spec/editor-component-spec.coffee | 7 +++++++ src/editor-component.coffee | 13 ++++++++++--- src/gutter-component.coffee | 26 +++++++++++++++++--------- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 17e068541..cca28a6cb 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -580,6 +580,13 @@ describe "EditorComponent", -> expect(verticalScrollbarNode.style.overflowX).toBe '' expect(horizontalScrollbarNode.style.overflowY).toBe 'hidden' + it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", -> + gutterNode = node.querySelector('.gutter') + node.style.width = 10 * charWidth + 'px' + component.measureHeightAndWidth() + + expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth() + describe "when a mousewheel event occurs on the editor", -> it "updates the horizontal or vertical scrollbar depending on which delta is greater (x or y)", -> node.style.height = 4.5 * lineHeightInPixels + 'px' diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 57d25623a..52898665f 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -20,10 +20,13 @@ EditorComponent = React.createClass cursorsMoved: false preservedRowRange: null scrollingVertically: false + gutterWidth: 0 render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide} = @state {editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props + maxLineNumberDigits = editor.getScreenLineCount().toString().length + if @isMounted() renderedRowRange = @getRenderedRowRange() scrollHeight = editor.getScrollHeight() @@ -37,8 +40,9 @@ EditorComponent = React.createClass div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1, GutterComponent { - editor, renderedRowRange, scrollTop, scrollHeight, - lineHeight: lineHeightInPixels, @pendingChanges + editor, renderedRowRange, maxLineNumberDigits, scrollTop, scrollHeight, + lineHeight: lineHeightInPixels, fontSize, fontFamily, @pendingChanges, + onWidthChanged: @onGutterWidthChanged } EditorScrollViewComponent { @@ -63,7 +67,7 @@ EditorComponent = React.createClass orientation: 'horizontal' onScroll: @onHorizontalScroll scrollLeft: scrollLeft - scrollWidth: scrollWidth + scrollWidth: scrollWidth + @gutterWidth scrollableInOppositeDirection: editor.verticallyScrollable() if @isMounted() getRenderedRowRange: -> @@ -327,6 +331,9 @@ EditorComponent = React.createClass onCursorsMoved: -> @cursorsMoved = true + onGutterWidthChanged: (@gutterWidth) -> + @requestUpdate() + requestUpdate: -> if @batchingUpdates @updateRequested = true diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index ef3529e79..16903d5fd 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -8,18 +8,19 @@ GutterComponent = React.createClass displayName: 'GutterComponent' mixins: [SubscriberMixin] + lastMeasuredWidth: null + render: -> div className: 'gutter', @renderLineNumbers() if @isMounted() renderLineNumbers: -> - {editor, renderedRowRange, scrollTop, scrollHeight} = @props + {editor, renderedRowRange, maxLineNumberDigits, scrollTop, scrollHeight} = @props [startRow, endRow] = renderedRowRange charWidth = editor.getDefaultCharWidth() lineHeight = editor.getLineHeight() - maxDigits = editor.getScreenLineCount().toString().length style = - width: charWidth * (maxDigits + 1.5) + width: charWidth * (maxLineNumberDigits + 1.5) height: scrollHeight WebkitTransform: "translate3d(0, #{-scrollTop}px, 0)" @@ -35,7 +36,7 @@ GutterComponent = React.createClass key = tokenizedLines[i].id screenRow = startRow + i - lineNumbers.push(LineNumberComponent({key, lineNumber, maxDigits, bufferRow, screenRow, lineHeight})) + lineNumbers.push(LineNumberComponent({key, lineNumber, maxLineNumberDigits, bufferRow, screenRow, lineHeight})) lastBufferRow = bufferRow div className: 'line-numbers', style: style, @@ -45,7 +46,7 @@ GutterComponent = React.createClass # non-zero-delta change to the screen lines has occurred within the current # visible row range. shouldComponentUpdate: (newProps) -> - return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeight') + return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeight', 'fontSize') {renderedRowRange, pendingChanges} = newProps for change in pendingChanges when change.screenDelta > 0 or change.bufferDelta > 0 @@ -53,6 +54,13 @@ GutterComponent = React.createClass false + componentDidUpdate: (oldProps) -> + unless @lastMeasuredWidth? and isEqualForProperties(oldProps, @props, 'maxLineNumberDigits', 'fontSize', 'fontFamily') + width = @getDOMNode().offsetWidth + if width isnt @lastMeasuredWidth + @lastMeasuredWidth = width + @props.onWidthChanged(width) + LineNumberComponent = React.createClass displayName: 'LineNumberComponent' @@ -66,9 +74,9 @@ LineNumberComponent = React.createClass dangerouslySetInnerHTML: {__html: @buildInnerHTML()} buildInnerHTML: -> - {lineNumber, maxDigits} = @props - if lineNumber.length < maxDigits - padding = multiplyString(' ', maxDigits - lineNumber.length) + {lineNumber, maxLineNumberDigits} = @props + if lineNumber.length < maxLineNumberDigits + padding = multiplyString(' ', maxLineNumberDigits - lineNumber.length) padding + lineNumber + @iconDivHTML else lineNumber + @iconDivHTML @@ -76,4 +84,4 @@ LineNumberComponent = React.createClass iconDivHTML: '
' shouldComponentUpdate: (newProps) -> - not isEqualForProperties(newProps, @props, 'lineHeight', 'screenRow', 'maxDigits') + not isEqualForProperties(newProps, @props, 'lineHeight', 'screenRow', 'maxLineNumberDigits')