Constrain scrollTop based on clientHeight and scrollHeight

This commit is contained in:
Nathan Sobo
2015-02-04 09:38:00 -07:00
parent 3656d4cca6
commit 5bb3095ffa
2 changed files with 112 additions and 22 deletions

View File

@@ -223,11 +223,30 @@ describe "TextEditorPresenter", ->
describe ".scrollTop", ->
it "tracks the value of ::scrollTop", ->
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10)
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 20)
expect(presenter.state.verticalScrollbar.scrollTop).toBe 10
expectStateUpdate presenter, -> presenter.setScrollTop(50)
expect(presenter.state.verticalScrollbar.scrollTop).toBe 50
it "never exceeds the computed scroll height minus the computed client height", ->
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 50, horizontalScrollbarHeight: 10)
expectStateUpdate presenter, -> presenter.setScrollTop(100)
expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> presenter.setHeight(60)
expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(15)
expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]])
expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
# Scroll top only gets smaller when needed as dimensions change, never bigger
scrollTopBefore = presenter.state.verticalScrollbar.scrollTop
expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n')
expect(presenter.state.verticalScrollbar.scrollTop).toBe scrollTopBefore
describe ".content", ->
describe ".scrollingVertically", ->
it "is true for ::stoppedScrollingDelay milliseconds following a changes to ::scrollTop", ->
@@ -323,11 +342,30 @@ describe "TextEditorPresenter", ->
describe ".scrollTop", ->
it "tracks the value of ::scrollTop", ->
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10)
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 20)
expect(presenter.state.content.scrollTop).toBe 10
expectStateUpdate presenter, -> presenter.setScrollTop(50)
expect(presenter.state.content.scrollTop).toBe 50
it "never exceeds the computed scroll height minus the computed client height", ->
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 50, horizontalScrollbarHeight: 10)
expectStateUpdate presenter, -> presenter.setScrollTop(100)
expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> presenter.setHeight(60)
expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(15)
expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]])
expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
# Scroll top only gets smaller when needed as dimensions change, never bigger
scrollTopBefore = presenter.state.verticalScrollbar.scrollTop
expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n')
expect(presenter.state.content.scrollTop).toBe scrollTopBefore
describe ".scrollLeft", ->
it "tracks the value of ::scrollLeft", ->
presenter = new TextEditorPresenter(model: editor, scrollLeft: 10, lineHeight: 10, lineOverdrawMargin: 1)
@@ -1366,11 +1404,30 @@ describe "TextEditorPresenter", ->
describe ".scrollTop", ->
it "tracks the value of ::scrollTop", ->
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10)
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 20)
expect(presenter.state.gutter.scrollTop).toBe 10
expectStateUpdate presenter, -> presenter.setScrollTop(50)
expect(presenter.state.gutter.scrollTop).toBe 50
it "never exceeds the computed scroll height minus the computed client height", ->
presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 50, horizontalScrollbarHeight: 10)
expectStateUpdate presenter, -> presenter.setScrollTop(100)
expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> presenter.setHeight(60)
expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(15)
expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]])
expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight()
# Scroll top only gets smaller when needed as dimensions change, never bigger
scrollTopBefore = presenter.state.verticalScrollbar.scrollTop
expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n')
expect(presenter.state.gutter.scrollTop).toBe scrollTopBefore
describe ".backgroundColor", ->
it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", ->
presenter = new TextEditorPresenter(model: editor, backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)")

View File

@@ -96,7 +96,7 @@ class TextEditorPresenter
@state.gutter.scrollHeight = scrollHeight
@state.verticalScrollbar.scrollHeight = scrollHeight
scrollTop = @scrollTop
scrollTop = @computeScrollTop()
@state.content.scrollTop = scrollTop
@state.gutter.scrollTop = scrollTop
@state.verticalScrollbar.scrollTop = scrollTop
@@ -114,26 +114,16 @@ class TextEditorPresenter
@emitter.emit 'did-update-state'
updateScrollbarsState: ->
contentWidth = @computeContentWidth()
contentHeight = @computeContentHeight()
clientWidthWithoutVerticalScrollbar = @contentFrameWidth
clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth
clientHeightWithoutHorizontalScrollbar = @getHeight()
clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight
horizontalScrollbarVisible =
contentWidth > clientWidthWithoutVerticalScrollbar or
contentWidth > clientWidthWithVerticalScrollbar and contentHeight > clientHeightWithoutHorizontalScrollbar
verticalScrollbarVisible =
contentHeight > clientHeightWithoutHorizontalScrollbar or
contentHeight > clientHeightWithHorizontalScrollbar and contentWidth > clientWidthWithoutVerticalScrollbar
horizontalScrollbarHeight = @computeHorizontalScrollbarHeight()
verticalScrollbarWidth = @computeVerticalScrollbarWidth()
@state.horizontalScrollbar.visible = horizontalScrollbarVisible
@state.horizontalScrollbar.visible = horizontalScrollbarHeight > 0
@state.horizontalScrollbar.height = @horizontalScrollbarHeight
@state.horizontalScrollbar.right = if verticalScrollbarVisible then @verticalScrollbarWidth else 0
@state.horizontalScrollbar.right = verticalScrollbarWidth
@state.verticalScrollbar.visible = verticalScrollbarVisible
@state.verticalScrollbar.visible = verticalScrollbarWidth > 0
@state.verticalScrollbar.width = @verticalScrollbarWidth
@state.verticalScrollbar.bottom = if horizontalScrollbarVisible then @horizontalScrollbarHeight else 0
@state.verticalScrollbar.bottom = horizontalScrollbarHeight
@emitter.emit 'did-update-state'
@@ -324,11 +314,11 @@ class TextEditorPresenter
regions
computeStartRow: ->
startRow = Math.floor(@scrollTop / @lineHeight) - @lineOverdrawMargin
startRow = Math.floor(@computeScrollTop() / @lineHeight) - @lineOverdrawMargin
Math.max(0, startRow)
computeEndRow: ->
startRow = Math.floor(@scrollTop / @lineHeight)
startRow = Math.floor(@computeScrollTop() / @lineHeight)
visibleLinesCount = Math.ceil(@getHeight() / @lineHeight) + 1
endRow = startRow + visibleLinesCount + @lineOverdrawMargin
Math.min(@model.getScreenLineCount(), endRow)
@@ -347,6 +337,49 @@ class TextEditorPresenter
computeContentHeight: ->
@lineHeight * @model.getScreenLineCount()
computeClientHeight: ->
@getHeight() - @computeHorizontalScrollbarHeight()
computeClientWidth: ->
@contentFrameWidth - @computeVerticalScrollbarWidth()
computeScrollTop: ->
@scrollTop = Math.min(@scrollTop, @computeScrollHeight() - @computeClientHeight())
computeHorizontalScrollbarHeight: ->
contentWidth = @computeContentWidth()
contentHeight = @computeContentHeight()
clientWidthWithoutVerticalScrollbar = @contentFrameWidth
clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth
clientHeightWithoutHorizontalScrollbar = @getHeight()
clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight
horizontalScrollbarVisible =
contentWidth > clientWidthWithoutVerticalScrollbar or
contentWidth > clientWidthWithVerticalScrollbar and contentHeight > clientHeightWithoutHorizontalScrollbar
if horizontalScrollbarVisible
@horizontalScrollbarHeight
else
0
computeVerticalScrollbarWidth: ->
contentWidth = @computeContentWidth()
contentHeight = @computeContentHeight()
clientWidthWithoutVerticalScrollbar = @contentFrameWidth
clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth
clientHeightWithoutHorizontalScrollbar = @getHeight()
clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight
verticalScrollbarVisible =
contentHeight > clientHeightWithoutHorizontalScrollbar or
contentHeight > clientHeightWithHorizontalScrollbar and contentWidth > clientWidthWithoutVerticalScrollbar
if verticalScrollbarVisible
@verticalScrollbarWidth
else
0
lineDecorationClassesForRow: (row) ->
return null if @model.isMini()