mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Maintain scroll history
This commit is contained in:
@@ -3605,6 +3605,18 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
expect(wrapperNode.getScrollTop()).toBe 0
|
||||
|
||||
describe "when many scroll events are triggered before an animation frame", ->
|
||||
it "applies them sequentially, as if they were not batched", ->
|
||||
wrapperNode.style.height = lineHeightInPixels * 7 + "px"
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
editor.scrollToScreenPosition([8, 0])
|
||||
editor.scrollToScreenPosition([8, 0], center: true)
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(wrapperNode.getScrollTop()).toBe(4 * lineHeightInPixels)
|
||||
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "clips pixel positions above buffer start", ->
|
||||
expect(component.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]
|
||||
|
||||
@@ -1893,9 +1893,9 @@ describe "TextEditorPresenter", ->
|
||||
pixelPosition: {top: 6 * 10 - scrollTop, left: gutterWidth}
|
||||
}
|
||||
|
||||
editor.insertNewline()
|
||||
presenter.getState() # forces scroll top to be changed
|
||||
presenter.setScrollTop(scrollTop) # I'm fighting the editor
|
||||
expectStateUpdate presenter, ->
|
||||
editor.insertNewline()
|
||||
presenter.setScrollTop(scrollTop) # I'm fighting the editor
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
|
||||
@@ -317,7 +317,7 @@ class TextEditorComponent
|
||||
pendingScrollTop = @pendingScrollTop
|
||||
@pendingScrollTop = null
|
||||
@presenter.setScrollTop(pendingScrollTop)
|
||||
@presenter.commitPendingScrollTopPosition()
|
||||
@presenter.commitScrollQueue()
|
||||
|
||||
onHorizontalScroll: (scrollLeft) =>
|
||||
return if @updateRequested or scrollLeft is @presenter.getScrollLeft()
|
||||
@@ -327,7 +327,7 @@ class TextEditorComponent
|
||||
unless animationFramePending
|
||||
@requestAnimationFrame =>
|
||||
@presenter.setScrollLeft(@pendingScrollLeft)
|
||||
@presenter.commitPendingScrollLeftPosition()
|
||||
@presenter.commitScrollQueue()
|
||||
@pendingScrollLeft = null
|
||||
|
||||
onMouseWheel: (event) =>
|
||||
@@ -613,11 +613,11 @@ class TextEditorComponent
|
||||
|
||||
if mouseYDelta?
|
||||
@presenter.setScrollTop(@presenter.getScrollTop() + yDirection * scaleScrollDelta(mouseYDelta))
|
||||
@presenter.commitPendingScrollTopPosition()
|
||||
@presenter.commitScrollQueue()
|
||||
|
||||
if mouseXDelta?
|
||||
@presenter.setScrollLeft(@presenter.getScrollLeft() + xDirection * scaleScrollDelta(mouseXDelta))
|
||||
@presenter.commitPendingScrollLeftPosition()
|
||||
@presenter.commitScrollQueue()
|
||||
|
||||
scaleScrollDelta = (scrollDelta) ->
|
||||
Math.pow(scrollDelta / 2, 3) / 280
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
_ = require 'underscore-plus'
|
||||
Decoration = require './decoration'
|
||||
|
||||
class ScrollQueue
|
||||
constructor: ->
|
||||
@queue = []
|
||||
@committing = false
|
||||
|
||||
enqueue: (scrollAction) ->
|
||||
if @committing
|
||||
@queue.unshift(scrollAction)
|
||||
else
|
||||
@queue.push(scrollAction)
|
||||
|
||||
commit: ->
|
||||
@committing = true
|
||||
while scrollAction = @queue.shift()
|
||||
scrollAction()
|
||||
@committing = false
|
||||
|
||||
module.exports =
|
||||
class TextEditorPresenter
|
||||
toggleCursorBlinkHandle: null
|
||||
@@ -22,6 +39,7 @@ class TextEditorPresenter
|
||||
@measuredVerticalScrollbarWidth = verticalScrollbarWidth
|
||||
@gutterWidth ?= 0
|
||||
@tileSize ?= 6
|
||||
@scrollQueue = new ScrollQueue
|
||||
|
||||
@disposables = new CompositeDisposable
|
||||
@emitter = new Emitter
|
||||
@@ -816,7 +834,7 @@ class TextEditorPresenter
|
||||
setScrollTop: (scrollTop) ->
|
||||
return unless scrollTop?
|
||||
|
||||
@pendingScrollTop = scrollTop
|
||||
@scrollQueue.enqueue => @commitScrollTop(scrollTop)
|
||||
|
||||
@shouldUpdateVerticalScrollState = true
|
||||
@shouldUpdateHiddenInputState = true
|
||||
@@ -856,7 +874,7 @@ class TextEditorPresenter
|
||||
setScrollLeft: (scrollLeft) ->
|
||||
return unless scrollLeft?
|
||||
|
||||
@pendingScrollLeft = scrollLeft
|
||||
@scrollQueue.enqueue => @commitScrollLeft(scrollLeft)
|
||||
|
||||
@shouldUpdateHorizontalScrollState = true
|
||||
@shouldUpdateHiddenInputState = true
|
||||
@@ -1473,7 +1491,54 @@ class TextEditorPresenter
|
||||
@emitDidUpdateState()
|
||||
|
||||
didChangeScrollPosition: (position) ->
|
||||
@pendingScrollLogicalPosition = position
|
||||
@scrollQueue.enqueue =>
|
||||
{screenRange, options} = position
|
||||
|
||||
verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels()
|
||||
horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels()
|
||||
|
||||
{top, left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start))
|
||||
{top: endTop, left: endLeft, height: endHeight} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end))
|
||||
bottom = endTop + endHeight
|
||||
right = endLeft
|
||||
|
||||
top += @getScrollTop()
|
||||
bottom += @getScrollTop()
|
||||
left += @getScrollLeft()
|
||||
right += @getScrollLeft()
|
||||
|
||||
if options?.center
|
||||
desiredScrollCenter = (top + bottom) / 2
|
||||
unless @getScrollTop() < desiredScrollCenter < @getScrollBottom()
|
||||
desiredScrollTop = desiredScrollCenter - @getClientHeight() / 2
|
||||
desiredScrollBottom = desiredScrollCenter + @getClientHeight() / 2
|
||||
else
|
||||
desiredScrollTop = top - verticalScrollMarginInPixels
|
||||
desiredScrollBottom = bottom + verticalScrollMarginInPixels
|
||||
|
||||
desiredScrollLeft = left - horizontalScrollMarginInPixels
|
||||
desiredScrollRight = right + horizontalScrollMarginInPixels
|
||||
|
||||
if options?.reversed ? true
|
||||
if desiredScrollBottom > @getScrollBottom()
|
||||
@setScrollBottom(desiredScrollBottom)
|
||||
if desiredScrollTop < @getScrollTop()
|
||||
@setScrollTop(desiredScrollTop)
|
||||
|
||||
if desiredScrollRight > @getScrollRight()
|
||||
@setScrollRight(desiredScrollRight)
|
||||
if desiredScrollLeft < @getScrollLeft()
|
||||
@setScrollLeft(desiredScrollLeft)
|
||||
else
|
||||
if desiredScrollTop < @getScrollTop()
|
||||
@setScrollTop(desiredScrollTop)
|
||||
if desiredScrollBottom > @getScrollBottom()
|
||||
@setScrollBottom(desiredScrollBottom)
|
||||
|
||||
if desiredScrollLeft < @getScrollLeft()
|
||||
@setScrollLeft(desiredScrollLeft)
|
||||
if desiredScrollRight > @getScrollRight()
|
||||
@setScrollRight(desiredScrollRight)
|
||||
|
||||
@shouldUpdateCursorsState = true
|
||||
@shouldUpdateCustomGutterDecorationState = true
|
||||
@@ -1500,63 +1565,10 @@ class TextEditorPresenter
|
||||
getHorizontalScrollbarHeight: ->
|
||||
@horizontalScrollbarHeight
|
||||
|
||||
commitPendingLogicalScrollPosition: ->
|
||||
return unless @pendingScrollLogicalPosition?
|
||||
commitScrollLeft: (scrollLeft) ->
|
||||
return unless scrollLeft?
|
||||
|
||||
{screenRange, options} = @pendingScrollLogicalPosition
|
||||
|
||||
verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels()
|
||||
horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels()
|
||||
|
||||
{top, left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start))
|
||||
{top: endTop, left: endLeft, height: endHeight} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end))
|
||||
bottom = endTop + endHeight
|
||||
right = endLeft
|
||||
|
||||
top += @scrollTop
|
||||
bottom += @scrollTop
|
||||
left += @scrollLeft
|
||||
right += @scrollLeft
|
||||
|
||||
if options?.center
|
||||
desiredScrollCenter = (top + bottom) / 2
|
||||
unless @getScrollTop() < desiredScrollCenter < @getScrollBottom()
|
||||
desiredScrollTop = desiredScrollCenter - @getClientHeight() / 2
|
||||
desiredScrollBottom = desiredScrollCenter + @getClientHeight() / 2
|
||||
else
|
||||
desiredScrollTop = top - verticalScrollMarginInPixels
|
||||
desiredScrollBottom = bottom + verticalScrollMarginInPixels
|
||||
|
||||
desiredScrollLeft = left - horizontalScrollMarginInPixels
|
||||
desiredScrollRight = right + horizontalScrollMarginInPixels
|
||||
|
||||
if options?.reversed ? true
|
||||
if desiredScrollBottom > @getScrollBottom()
|
||||
@setScrollBottom(desiredScrollBottom)
|
||||
if desiredScrollTop < @getScrollTop()
|
||||
@setScrollTop(desiredScrollTop)
|
||||
|
||||
if desiredScrollRight > @getScrollRight()
|
||||
@setScrollRight(desiredScrollRight)
|
||||
if desiredScrollLeft < @getScrollLeft()
|
||||
@setScrollLeft(desiredScrollLeft)
|
||||
else
|
||||
if desiredScrollTop < @getScrollTop()
|
||||
@setScrollTop(desiredScrollTop)
|
||||
if desiredScrollBottom > @getScrollBottom()
|
||||
@setScrollBottom(desiredScrollBottom)
|
||||
|
||||
if desiredScrollLeft < @getScrollLeft()
|
||||
@setScrollLeft(desiredScrollLeft)
|
||||
if desiredScrollRight > @getScrollRight()
|
||||
@setScrollRight(desiredScrollRight)
|
||||
|
||||
@pendingScrollLogicalPosition = null
|
||||
|
||||
commitPendingScrollLeftPosition: ->
|
||||
return unless @pendingScrollLeft?
|
||||
|
||||
scrollLeft = @constrainScrollLeft(@pendingScrollLeft)
|
||||
scrollLeft = @constrainScrollLeft(scrollLeft)
|
||||
if scrollLeft isnt @scrollLeft and not Number.isNaN(scrollLeft)
|
||||
@realScrollLeft = scrollLeft
|
||||
@scrollLeft = Math.round(scrollLeft)
|
||||
@@ -1567,10 +1579,10 @@ class TextEditorPresenter
|
||||
|
||||
@pendingScrollLeft = null
|
||||
|
||||
commitPendingScrollTopPosition: ->
|
||||
return unless @pendingScrollTop?
|
||||
commitScrollTop: (scrollTop) ->
|
||||
return unless scrollTop?
|
||||
|
||||
scrollTop = @constrainScrollTop(@pendingScrollTop)
|
||||
scrollTop = @constrainScrollTop(scrollTop)
|
||||
if scrollTop isnt @scrollTop and not Number.isNaN(scrollTop)
|
||||
@realScrollTop = scrollTop
|
||||
@scrollTop = Math.round(scrollTop)
|
||||
@@ -1580,10 +1592,8 @@ class TextEditorPresenter
|
||||
@didStartScrolling()
|
||||
@emitter.emit 'did-change-scroll-top', @scrollTop
|
||||
|
||||
@pendingScrollTop = null
|
||||
|
||||
restoreScrollPosition: ->
|
||||
return if @hasRestoredScrollPosition or not @hasPixelPositionRequirements()
|
||||
return unless @hasPixelPositionRequirements()
|
||||
|
||||
@setScrollTop(@scrollRow * @lineHeight) if @scrollRow?
|
||||
@setScrollLeft(@scrollColumn * @baseCharacterWidth) if @scrollColumn?
|
||||
@@ -1591,10 +1601,11 @@ class TextEditorPresenter
|
||||
@hasRestoredScrollPosition = true
|
||||
|
||||
updateScrollPosition: ->
|
||||
@restoreScrollPosition()
|
||||
@commitPendingLogicalScrollPosition()
|
||||
@commitPendingScrollLeftPosition()
|
||||
@commitPendingScrollTopPosition()
|
||||
@restoreScrollPosition() unless @hasRestoredScrollPosition
|
||||
@commitScrollQueue()
|
||||
|
||||
commitScrollQueue: ->
|
||||
@scrollQueue.commit()
|
||||
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
@emitter.on 'did-change-scroll-top', callback
|
||||
|
||||
Reference in New Issue
Block a user