Don't obscure last character of long lines with vertical scrollbar

This entailed quite a few changes to dial in scrollbars. The scrollbars
are now adjusted in size to account for the width of the opposite
scrollbar. If the width or height are not explicitly constrained and we
are scrollable in the opposite direction that is constrained, we account
for the width of the opposite scrollbar in assigning a natural height
or width based on the content.
This commit is contained in:
Nathan Sobo
2014-04-24 21:20:25 -06:00
parent 527ada47f9
commit dbd271f70a
6 changed files with 87 additions and 25 deletions

View File

@@ -561,24 +561,38 @@ describe "EditorComponent", ->
bottomOfEditor = node.getBoundingClientRect().bottom
expect(bottomOfLastLine).toBe bottomOfEditor
it "assigns the overflow to 'hidden' in the opposite direction unless the editor scrollable in that direction", ->
expect(verticalScrollbarNode.style.overflowX).toBe 'hidden'
expect(horizontalScrollbarNode.style.overflowY).toBe 'hidden'
it "does not obscure the last character of the longest line with the vertical scrollbar", ->
node.style.height = 7 * lineHeightInPixels + 'px'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
editor.setScrollLeft(Infinity)
lineNodes = node.querySelectorAll('.line')
rightOfLongestLine = lineNodes[6].getBoundingClientRect().right
leftOfVerticalScrollbar = verticalScrollbarNode.getBoundingClientRect().left
expect(rightOfLongestLine).toBe leftOfVerticalScrollbar - 1 # Leave 1 px so the cursor is visible on the end of the line
it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", ->
expect(verticalScrollbarNode.style.bottom).toBe ''
expect(horizontalScrollbarNode.style.right).toBe ''
node.style.height = 4.5 * lineHeightInPixels + 'px'
node.style.width = '1000px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.overflowX).toBe 'hidden'
expect(horizontalScrollbarNode.style.overflowY).toBe ''
expect(verticalScrollbarNode.style.bottom).toBe ''
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.overflowX).toBe ''
expect(horizontalScrollbarNode.style.overflowY).toBe ''
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
node.style.height = 20 * lineHeightInPixels + 'px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.overflowX).toBe ''
expect(horizontalScrollbarNode.style.overflowY).toBe 'hidden'
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
expect(horizontalScrollbarNode.style.right).toBe ''
it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
gutterNode = node.querySelector('.gutter')

View File

@@ -33,6 +33,7 @@ class DisplayBuffer extends Model
verticalScrollMargin: 2
horizontalScrollMargin: 6
horizontalScrollbarHeight: 15
verticalScrollbarWidth: 15
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
super
@@ -112,27 +113,58 @@ class DisplayBuffer extends Model
getHorizontalScrollMargin: -> @horizontalScrollMargin
setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin
getHeight: -> @height ? @getScrollHeight()
getHorizontalScrollbarHeight: -> @horizontalScrollbarHeight
setHorizontalScrollbarHeight: (@horizontalScrollbarHeight) -> @horizontalScrollbarHeight
getVerticalScrollbarWidth: -> @verticalScrollbarWidth
setVerticalScrollbarWidth: (@verticalScrollbarWidth) -> @verticalScrollbarWidth
getHeight: ->
if @height?
@height
else
if @horizontallyScrollable()
@getScrollHeight() + @getHorizontalScrollbarHeight()
else
@getScrollHeight()
setHeight: (@height) -> @height
getClientHeight: ->
if @horizontallyScrollable()
getClientHeight: (reentrant) ->
if @horizontallyScrollable(reentrant)
@getHeight() - @getHorizontalScrollbarHeight()
else
@getHeight()
horizontallyScrollable: ->
not @getSoftWrap() and @getScrollWidth() > @getWidth()
getClientWidth: (reentrant) ->
if @verticallyScrollable(reentrant)
@getWidth() - @getVerticalScrollbarWidth()
else
@getWidth()
verticallyScrollable: ->
@getScrollHeight() > @getClientHeight()
horizontallyScrollable: (reentrant) ->
return false unless @width?
return false if @getSoftWrap()
if reentrant
@getScrollWidth() > @getWidth()
else
@getScrollWidth() > @getClientWidth(true)
getHorizontalScrollbarHeight: -> @horizontalScrollbarHeight
setHorizontalScrollbarHeight: (@horizontalScrollbarHeight) -> @horizontalScrollbarHeight
verticallyScrollable: (reentrant) ->
return false unless @height?
if reentrant
@getScrollHeight() > @getHeight()
else
@getScrollHeight() > @getClientHeight(true)
getWidth: ->
@width ? @getScrollWidth()
if @width?
@width
else
if @verticallyScrollable()
@getScrollWidth() + @getVerticalScrollbarWidth()
else
@getScrollWidth()
setWidth: (newWidth) ->
oldWidth = @width
@@ -156,7 +188,7 @@ class DisplayBuffer extends Model
getScrollLeft: -> @scrollLeft
setScrollLeft: (scrollLeft) ->
if @manageScrollPosition
@scrollLeft = Math.max(0, Math.min(@getScrollWidth() - @getWidth(), scrollLeft))
@scrollLeft = Math.max(0, Math.min(@getScrollWidth() - @getClientWidth(), scrollLeft))
@scrollLeft
else
@scrollLeft = scrollLeft
@@ -172,6 +204,8 @@ class DisplayBuffer extends Model
getDefaultCharWidth: -> @defaultCharWidth
setDefaultCharWidth: (@defaultCharWidth) -> @defaultCharWidth
getCursorWidth: -> 1
getScopedCharWidth: (scopeNames, char) ->
@getScopedCharWidths(scopeNames)[char]
@@ -199,7 +233,7 @@ class DisplayBuffer extends Model
@getLineCount() * @getLineHeight()
getScrollWidth: ->
@getMaxLineLength() * @getDefaultCharWidth()
(@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth()
getVisibleRowRange: ->
unless @getLineHeight() > 0

View File

@@ -34,6 +34,8 @@ EditorComponent = React.createClass
scrollTop = editor.getScrollTop()
scrollLeft = editor.getScrollLeft()
lineHeightInPixels = editor.getLineHeight()
horizontalScrollbarHeight = editor.getHorizontalScrollbarHeight()
verticalScrollbarWidth = editor.getVerticalScrollbarWidth()
className = 'editor editor-colors react'
className += ' is-focused' if focused
@@ -60,6 +62,7 @@ EditorComponent = React.createClass
scrollTop: scrollTop
scrollHeight: scrollHeight
scrollableInOppositeDirection: editor.horizontallyScrollable() if @isMounted()
horizontalScrollbarHeight: horizontalScrollbarHeight
ScrollbarComponent
ref: 'horizontalScrollbar'
@@ -69,6 +72,7 @@ EditorComponent = React.createClass
scrollLeft: scrollLeft
scrollWidth: scrollWidth + @gutterWidth
scrollableInOppositeDirection: editor.verticallyScrollable() if @isMounted()
verticalScrollbarWidth: verticalScrollbarWidth
getRenderedRowRange: ->
renderedRowRange = @props.editor.getVisibleRowRange()

View File

@@ -1895,8 +1895,12 @@ class Editor extends Model
verticallyScrollable: -> @displayBuffer.verticallyScrollable()
getHorizontalScrollbarHeight: -> @displayBuffer.getHorizontalScrollbarHeight()
setHorizontalScrollbarHeight: (height) -> @displayBuffer.setHorizontalScrollbarHeight(height)
getVerticalScrollbarWidth: -> @displayBuffer.getVerticalScrollbarWidth()
setVerticalScrollbarWidth: (width) -> @displayBuffer.setVerticalScrollbarWidth(width)
# Deprecated: Call {::joinLines} instead.
joinLine: ->
deprecate("Use Editor::joinLines() instead")

View File

@@ -5,14 +5,15 @@ React = require 'react'
module.exports =
ScrollbarComponent = React.createClass
render: ->
{orientation, className, scrollHeight, scrollWidth, scrollableInOppositeDirection} = @props
{orientation, className, scrollHeight, scrollWidth} = @props
{scrollableInOppositeDirection, horizontalScrollbarHeight, verticalScrollbarWidth} = @props
style = {}
switch orientation
when 'vertical'
style.overflowX = 'hidden' unless scrollableInOppositeDirection
style.bottom = horizontalScrollbarHeight if scrollableInOppositeDirection
when 'horizontal'
style.overflowY = 'hidden' unless scrollableInOppositeDirection
style.right = verticalScrollbarWidth if scrollableInOppositeDirection
div {className, style, @onScroll},
switch orientation

View File

@@ -24,6 +24,7 @@
height: 15px;
overflow-x: auto;
overflow-y: hidden;
z-index: 3;
.scrollbar-content {
@@ -31,6 +32,10 @@
}
}
.vertical-scrollbar {
overflow-x: hidden;
}
.scroll-view {
overflow: hidden;
}