mirror of
https://github.com/atom/atom.git
synced 2026-01-24 22:38:20 -05:00
Merge pull request #9444 from atom/mb-synchronous-scroll-position
Allow synchronous control of scroll position through TextEditor model
This commit is contained in:
@@ -25,27 +25,34 @@ describe "TextEditorPresenter", ->
|
||||
editor.destroy()
|
||||
buffer.destroy()
|
||||
|
||||
buildPresenter = (params={}) ->
|
||||
buildPresenterWithoutMeasurements = (params={}) ->
|
||||
_.defaults params,
|
||||
model: editor
|
||||
explicitHeight: 130
|
||||
contentFrameWidth: 500
|
||||
windowWidth: 500
|
||||
windowHeight: 130
|
||||
boundingClientRect: {left: 0, top: 0, width: 500, height: 130}
|
||||
gutterWidth: 0
|
||||
lineHeight: 10
|
||||
baseCharacterWidth: 10
|
||||
horizontalScrollbarHeight: 10
|
||||
verticalScrollbarWidth: 10
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
config: atom.config
|
||||
|
||||
contentFrameWidth: 500
|
||||
presenter = new TextEditorPresenter(params)
|
||||
presenter.setLinesYardstick(new FakeLinesYardstick(editor, presenter))
|
||||
presenter
|
||||
|
||||
buildPresenter = (params={}) ->
|
||||
presenter = buildPresenterWithoutMeasurements(params)
|
||||
presenter.setScrollTop(params.scrollTop) if params.scrollTop?
|
||||
presenter.setScrollLeft(params.scrollLeft) if params.scrollLeft?
|
||||
presenter.setExplicitHeight(params.explicitHeight ? 130)
|
||||
presenter.setWindowSize(params.windowWidth ? 500, params.windowHeight ? 130)
|
||||
presenter.setBoundingClientRect(params.boundingClientRect ? {
|
||||
left: 0
|
||||
top: 0
|
||||
width: 500
|
||||
height: 130
|
||||
})
|
||||
presenter.setGutterWidth(params.gutterWidth ? 0)
|
||||
presenter.setLineHeight(params.lineHeight ? 10)
|
||||
presenter.setBaseCharacterWidth(params.baseCharacterWidth ? 10)
|
||||
presenter.setHorizontalScrollbarHeight(params.horizontalScrollbarHeight ? 10)
|
||||
presenter.setVerticalScrollbarWidth(params.verticalScrollbarWidth ? 10)
|
||||
presenter
|
||||
|
||||
expectValues = (actual, expected) ->
|
||||
for key, value of expected
|
||||
expect(actual[key]).toEqual value
|
||||
@@ -167,16 +174,14 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateFn(presenter).tiles[12]).toBeDefined()
|
||||
|
||||
it "is empty until all of the required measurements are assigned", ->
|
||||
presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null)
|
||||
presenter = buildPresenterWithoutMeasurements()
|
||||
expect(stateFn(presenter).tiles).toEqual({})
|
||||
|
||||
presenter.setExplicitHeight(25)
|
||||
expect(stateFn(presenter).tiles).toEqual({})
|
||||
|
||||
# Sets scroll row from model's logical position
|
||||
presenter.setLineHeight(10)
|
||||
expect(stateFn(presenter).tiles).toEqual({})
|
||||
|
||||
presenter.setScrollTop(0)
|
||||
expect(stateFn(presenter).tiles).not.toEqual({})
|
||||
|
||||
it "updates when ::scrollTop changes", ->
|
||||
@@ -619,6 +624,8 @@ describe "TextEditorPresenter", ->
|
||||
describe ".scrollingVertically", ->
|
||||
it "is true for ::stoppedScrollingDelay milliseconds following a changes to ::scrollTop", ->
|
||||
presenter = buildPresenter(scrollTop: 10, stoppedScrollingDelay: 200, explicitHeight: 100)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe true
|
||||
advanceClock(300)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe false
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(0)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe true
|
||||
@@ -761,7 +768,8 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollTop).toBe(10)
|
||||
|
||||
it "corresponds to the passed logical coordinates when building the presenter", ->
|
||||
presenter = buildPresenter(scrollRow: 4, lineHeight: 10, explicitHeight: 20)
|
||||
editor.setFirstVisibleScreenRow(4)
|
||||
presenter = buildPresenter(lineHeight: 10, explicitHeight: 20)
|
||||
expect(presenter.getState().content.scrollTop).toBe(40)
|
||||
|
||||
it "tracks the value of ::scrollTop", ->
|
||||
@@ -775,11 +783,11 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(50)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollRow()).toBe(5)
|
||||
expect(editor.getFirstVisibleScreenRow()).toBe 5
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(57)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollRow()).toBe(6)
|
||||
expect(editor.getFirstVisibleScreenRow()).toBe 6
|
||||
|
||||
it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", ->
|
||||
presenter = buildPresenter(scrollTop: 80, lineHeight: 10, explicitHeight: 50, horizontalScrollbarHeight: 0)
|
||||
@@ -888,7 +896,8 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollLeft).toBe(50)
|
||||
|
||||
it "corresponds to the passed logical coordinates when building the presenter", ->
|
||||
presenter = buildPresenter(scrollColumn: 3, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
editor.setFirstVisibleScreenColumn(3)
|
||||
presenter = buildPresenter(lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
expect(presenter.getState().content.scrollLeft).toBe(30)
|
||||
|
||||
it "tracks the value of ::scrollLeft", ->
|
||||
@@ -902,11 +911,11 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(50)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollColumn()).toBe(5)
|
||||
expect(editor.getFirstVisibleScreenColumn()).toBe 5
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(57)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollColumn()).toBe(6)
|
||||
expect(editor.getFirstVisibleScreenColumn()).toBe 6
|
||||
|
||||
it "is always rounded to the nearest integer", ->
|
||||
presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
@@ -1006,20 +1015,25 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".backgroundColor", ->
|
||||
it "is assigned to ::backgroundColor unless the editor is mini", ->
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)'
|
||||
|
||||
editor.setMini(true)
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBeNull()
|
||||
|
||||
it "updates when ::backgroundColor changes", ->
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)'
|
||||
expectStateUpdate presenter, -> presenter.setBackgroundColor('rgba(0, 0, 255, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(0, 0, 255, 0)'
|
||||
|
||||
it "updates when ::mini changes", ->
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)'
|
||||
expectStateUpdate presenter, -> editor.setMini(true)
|
||||
expect(presenter.getState().content.backgroundColor).toBeNull()
|
||||
@@ -1047,6 +1061,7 @@ describe "TextEditorPresenter", ->
|
||||
describe "[tileId].lines[lineId]", -> # line state objects
|
||||
it "includes the state for visible lines in a tile", ->
|
||||
presenter = buildPresenter(explicitHeight: 3, scrollTop: 4, lineHeight: 1, tileSize: 3, stoppedScrollingDelay: 200)
|
||||
presenter.setExplicitHeight(3)
|
||||
|
||||
expect(lineStateForScreenRow(presenter, 2)).toBeUndefined()
|
||||
|
||||
@@ -1320,7 +1335,7 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateForCursor(presenter, 4)).toBeUndefined()
|
||||
|
||||
it "is empty until all of the required measurements are assigned", ->
|
||||
presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null, horizontalScrollbarHeight: null)
|
||||
presenter = buildPresenterWithoutMeasurements()
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setExplicitHeight(25)
|
||||
@@ -1335,6 +1350,15 @@ describe "TextEditorPresenter", ->
|
||||
presenter.setBaseCharacterWidth(8)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setBoundingClientRect(top: 0, left: 0, width: 500, height: 130)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setWindowSize(500, 130)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setVerticalScrollbarWidth(10)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setHorizontalScrollbarHeight(10)
|
||||
expect(presenter.getState().content.cursors).not.toEqual({})
|
||||
|
||||
@@ -1466,7 +1490,8 @@ describe "TextEditorPresenter", ->
|
||||
it "alternates between true and false twice per ::cursorBlinkPeriod when the editor is focused", ->
|
||||
cursorBlinkPeriod = 100
|
||||
cursorBlinkResumeDelay = 200
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay, focused: true})
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay})
|
||||
presenter.setFocused(true)
|
||||
|
||||
expect(presenter.getState().content.cursorsVisible).toBe true
|
||||
expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2)
|
||||
@@ -1493,7 +1518,8 @@ describe "TextEditorPresenter", ->
|
||||
it "stops alternating for ::cursorBlinkResumeDelay when a cursor moves or a cursor is added", ->
|
||||
cursorBlinkPeriod = 100
|
||||
cursorBlinkResumeDelay = 200
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay, focused: true})
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay})
|
||||
presenter.setFocused(true)
|
||||
|
||||
expect(presenter.getState().content.cursorsVisible).toBe true
|
||||
expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2)
|
||||
@@ -1643,7 +1669,7 @@ describe "TextEditorPresenter", ->
|
||||
[[0, 2], [2, 4]],
|
||||
])
|
||||
|
||||
presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null, tileSize: 2)
|
||||
presenter = buildPresenterWithoutMeasurements(tileSize: 2)
|
||||
for tileId, tileState of presenter.getState().content.tiles
|
||||
expect(tileState.highlights).toEqual({})
|
||||
|
||||
@@ -1970,7 +1996,7 @@ describe "TextEditorPresenter", ->
|
||||
marker = editor.markBufferRange([[2, 13], [4, 14]], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})
|
||||
|
||||
presenter = buildPresenter(baseCharacterWidth: null, lineHeight: null, windowWidth: null, windowHeight: null, boundingClientRect: null)
|
||||
presenter = buildPresenterWithoutMeasurements()
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setBaseCharacterWidth(10)
|
||||
@@ -1982,6 +2008,12 @@ describe "TextEditorPresenter", ->
|
||||
presenter.setWindowSize(500, 100)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setVerticalScrollbarWidth(10)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setHorizontalScrollbarHeight(10)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setBoundingClientRect({top: 0, left: 0, height: 100, width: 500})
|
||||
expect(presenter.getState().content.overlays).not.toEqual({})
|
||||
|
||||
@@ -2168,7 +2200,8 @@ describe "TextEditorPresenter", ->
|
||||
expect(editor.getRowsPerPage()).toBe(20)
|
||||
|
||||
it "tracks the computed content height if ::autoHeight is true so the editor auto-expands vertically", ->
|
||||
presenter = buildPresenter(explicitHeight: null, autoHeight: true)
|
||||
presenter = buildPresenter(explicitHeight: null)
|
||||
presenter.setAutoHeight(true)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setAutoHeight(false)
|
||||
@@ -2185,7 +2218,9 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".focused", ->
|
||||
it "tracks the value of ::focused", ->
|
||||
presenter = buildPresenter(focused: false)
|
||||
presenter = buildPresenter()
|
||||
presenter.setFocused(false)
|
||||
|
||||
expect(presenter.getState().focused).toBe false
|
||||
expectStateUpdate presenter, -> presenter.setFocused(true)
|
||||
expect(presenter.getState().focused).toBe true
|
||||
@@ -2882,7 +2917,9 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".backgroundColor", ->
|
||||
it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", ->
|
||||
presenter = buildPresenter(backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)")
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor("rgba(255, 0, 0, 0)")
|
||||
presenter.setGutterBackgroundColor("rgba(0, 255, 0, 0)")
|
||||
expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 255, 0, 0)"
|
||||
expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 255, 0, 0)"
|
||||
|
||||
|
||||
@@ -5444,6 +5444,73 @@ describe "TextEditor", ->
|
||||
editor.selectPageUp()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [12, 2]]]
|
||||
|
||||
describe "::setFirstVisibleScreenRow() and ::getFirstVisibleScreenRow()", ->
|
||||
beforeEach ->
|
||||
line = Array(9).join('0123456789')
|
||||
editor.setText([1..100].map(-> line).join('\n'))
|
||||
expect(editor.getLineCount()).toBe 100
|
||||
expect(editor.lineTextForBufferRow(0).length).toBe 80
|
||||
|
||||
describe "when the editor doesn't have a height and lineHeightInPixels", ->
|
||||
it "does not affect the editor's visible row range", ->
|
||||
expect(editor.getVisibleRowRange()).toBeNull()
|
||||
|
||||
editor.setFirstVisibleScreenRow(1)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 1
|
||||
|
||||
editor.setFirstVisibleScreenRow(3)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 3
|
||||
|
||||
expect(editor.getVisibleRowRange()).toBeNull()
|
||||
expect(editor.getLastVisibleScreenRow()).toBeNull()
|
||||
|
||||
describe "when the editor has a height and lineHeightInPixels", ->
|
||||
beforeEach ->
|
||||
atom.config.set('editor.scrollPastEnd', true)
|
||||
editor.setHeight(100, true)
|
||||
editor.setLineHeightInPixels(10)
|
||||
|
||||
it "updates the editor's visible row range", ->
|
||||
editor.setFirstVisibleScreenRow(2)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 2
|
||||
expect(editor.getLastVisibleScreenRow()).toBe 12
|
||||
expect(editor.getVisibleRowRange()).toEqual [2, 12]
|
||||
|
||||
it "notifies ::onDidChangeFirstVisibleScreenRow observers", ->
|
||||
changeCount = 0
|
||||
editor.onDidChangeFirstVisibleScreenRow -> changeCount++
|
||||
|
||||
editor.setFirstVisibleScreenRow(2)
|
||||
expect(changeCount).toBe 1
|
||||
|
||||
editor.setFirstVisibleScreenRow(2)
|
||||
expect(changeCount).toBe 1
|
||||
|
||||
editor.setFirstVisibleScreenRow(3)
|
||||
expect(changeCount).toBe 2
|
||||
|
||||
it "ensures that the top row is less than the buffer's line count", ->
|
||||
editor.setFirstVisibleScreenRow(102)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 99
|
||||
expect(editor.getVisibleRowRange()).toEqual [99, 99]
|
||||
|
||||
it "ensures that the left column is less than the length of the longest screen line", ->
|
||||
editor.setFirstVisibleScreenRow(10)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 10
|
||||
|
||||
editor.setText("\n\n\n")
|
||||
|
||||
editor.setFirstVisibleScreenRow(10)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 3
|
||||
|
||||
describe "when the 'editor.scrollPastEnd' option is set to false", ->
|
||||
it "ensures that the bottom row is less than the buffer's line count", ->
|
||||
atom.config.set('editor.scrollPastEnd', false)
|
||||
|
||||
editor.setFirstVisibleScreenRow(95)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 89
|
||||
expect(editor.getVisibleRowRange()).toEqual [89, 99]
|
||||
|
||||
describe '.get/setPlaceholderText()', ->
|
||||
it 'can be created with placeholderText', ->
|
||||
newEditor = atom.workspace.buildTextEditor(
|
||||
|
||||
@@ -50,10 +50,6 @@ class TextEditorComponent
|
||||
|
||||
@presenter = new TextEditorPresenter
|
||||
model: @editor
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
scrollRow: @editor.getScrollRow()
|
||||
scrollColumn: @editor.getScrollColumn()
|
||||
tileSize: tileSize
|
||||
cursorBlinkPeriod: @cursorBlinkPeriod
|
||||
cursorBlinkResumeDelay: @cursorBlinkResumeDelay
|
||||
|
||||
@@ -13,15 +13,12 @@ class TextEditorPresenter
|
||||
minimumReflowInterval: 200
|
||||
|
||||
constructor: (params) ->
|
||||
{@model, @config, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @scrollColumn, @scrollRow, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params
|
||||
{horizontalScrollbarHeight, verticalScrollbarWidth} = params
|
||||
{@lineHeight, @baseCharacterWidth, @backgroundColor, @gutterBackgroundColor, @tileSize} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @focused} = params
|
||||
@measuredHorizontalScrollbarHeight = horizontalScrollbarHeight
|
||||
@measuredVerticalScrollbarWidth = verticalScrollbarWidth
|
||||
@gutterWidth ?= 0
|
||||
@tileSize ?= 6
|
||||
{@model, @config} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params
|
||||
{@contentFrameWidth} = params
|
||||
|
||||
@gutterWidth = 0
|
||||
@tileSize ?= 6
|
||||
@realScrollTop = @scrollTop
|
||||
@realScrollLeft = @scrollLeft
|
||||
@disposables = new CompositeDisposable
|
||||
@@ -77,7 +74,6 @@ class TextEditorPresenter
|
||||
@updateVerticalDimensions()
|
||||
@updateScrollbarDimensions()
|
||||
|
||||
@restoreScrollPosition()
|
||||
@commitPendingLogicalScrollTopPosition()
|
||||
@commitPendingScrollTopPosition()
|
||||
|
||||
@@ -218,6 +214,7 @@ class TextEditorPresenter
|
||||
|
||||
@disposables.add @model.onDidAddCursor(@didAddCursor.bind(this))
|
||||
@disposables.add @model.onDidRequestAutoscroll(@requestAutoscroll.bind(this))
|
||||
@disposables.add @model.onDidChangeFirstVisibleScreenRow(@didChangeFirstVisibleScreenRow.bind(this))
|
||||
@observeCursor(cursor) for cursor in @model.getCursors()
|
||||
@disposables.add @model.onDidAddGutter(@didAddGutter.bind(this))
|
||||
return
|
||||
@@ -778,8 +775,7 @@ class TextEditorPresenter
|
||||
if scrollTop isnt @realScrollTop and not Number.isNaN(scrollTop)
|
||||
@realScrollTop = scrollTop
|
||||
@scrollTop = Math.round(scrollTop)
|
||||
@scrollRow = Math.round(@scrollTop / @lineHeight)
|
||||
@model.setScrollRow(@scrollRow)
|
||||
@model.setFirstVisibleScreenRow(Math.round(@scrollTop / @lineHeight), true)
|
||||
|
||||
@updateStartRow()
|
||||
@updateEndRow()
|
||||
@@ -795,8 +791,7 @@ class TextEditorPresenter
|
||||
if scrollLeft isnt @realScrollLeft and not Number.isNaN(scrollLeft)
|
||||
@realScrollLeft = scrollLeft
|
||||
@scrollLeft = Math.round(scrollLeft)
|
||||
@scrollColumn = Math.round(@scrollLeft / @baseCharacterWidth)
|
||||
@model.setScrollColumn(@scrollColumn)
|
||||
@model.setFirstVisibleScreenColumn(Math.round(@scrollLeft / @baseCharacterWidth))
|
||||
|
||||
@emitter.emit 'did-change-scroll-left', @scrollLeft
|
||||
|
||||
@@ -1095,6 +1090,7 @@ class TextEditorPresenter
|
||||
setLineHeight: (lineHeight) ->
|
||||
unless @lineHeight is lineHeight
|
||||
@lineHeight = lineHeight
|
||||
@restoreScrollTopIfNeeded()
|
||||
@model.setLineHeightInPixels(lineHeight)
|
||||
@shouldUpdateHeightState = true
|
||||
@shouldUpdateHorizontalScrollState = true
|
||||
@@ -1122,6 +1118,7 @@ class TextEditorPresenter
|
||||
@halfWidthCharWidth = halfWidthCharWidth
|
||||
@koreanCharWidth = koreanCharWidth
|
||||
@model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth)
|
||||
@restoreScrollLeftIfNeeded()
|
||||
@characterWidthsChanged()
|
||||
|
||||
characterWidthsChanged: ->
|
||||
@@ -1433,6 +1430,9 @@ class TextEditorPresenter
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
didChangeFirstVisibleScreenRow: (screenRow) ->
|
||||
@updateScrollTop(screenRow * @lineHeight)
|
||||
|
||||
getVerticalScrollMarginInPixels: ->
|
||||
Math.round(@model.getVerticalScrollMargin() * @lineHeight)
|
||||
|
||||
@@ -1512,14 +1512,6 @@ class TextEditorPresenter
|
||||
@updateScrollTop(@pendingScrollTop)
|
||||
@pendingScrollTop = null
|
||||
|
||||
restoreScrollPosition: ->
|
||||
return if @hasRestoredScrollPosition or not @hasPixelPositionRequirements()
|
||||
|
||||
@setScrollTop(@scrollRow * @lineHeight) if @scrollRow?
|
||||
@setScrollLeft(@scrollColumn * @baseCharacterWidth) if @scrollColumn?
|
||||
|
||||
@hasRestoredScrollPosition = true
|
||||
|
||||
clearPendingScrollPosition: ->
|
||||
@pendingScrollLogicalPosition = null
|
||||
@pendingScrollTop = null
|
||||
@@ -1531,6 +1523,14 @@ class TextEditorPresenter
|
||||
canScrollTopTo: (scrollTop) ->
|
||||
@scrollTop isnt @constrainScrollTop(scrollTop)
|
||||
|
||||
restoreScrollTopIfNeeded: ->
|
||||
unless @scrollTop?
|
||||
@updateScrollTop(@model.getFirstVisibleScreenRow() * @lineHeight)
|
||||
|
||||
restoreScrollLeftIfNeeded: ->
|
||||
unless @scrollLeft?
|
||||
@updateScrollLeft(@model.getFirstVisibleScreenColumn() * @baseCharacterWidth)
|
||||
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
@emitter.on 'did-change-scroll-top', callback
|
||||
|
||||
|
||||
@@ -54,13 +54,11 @@ GutterContainer = require './gutter-container'
|
||||
# soft wraps and folds to ensure your code interacts with them correctly.
|
||||
module.exports =
|
||||
class TextEditor extends Model
|
||||
callDisplayBufferCreatedHook: false
|
||||
buffer: null
|
||||
languageMode: null
|
||||
cursors: null
|
||||
selections: null
|
||||
suppressSelectionMerging: false
|
||||
updateBatchDepth: 0
|
||||
selectionFlashDuration: 500
|
||||
gutterContainer: null
|
||||
|
||||
@@ -90,7 +88,7 @@ class TextEditor extends Model
|
||||
super
|
||||
|
||||
{
|
||||
@softTabs, @scrollRow, @scrollColumn, initialLine, initialColumn, tabLength,
|
||||
@softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength,
|
||||
softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config,
|
||||
@notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry,
|
||||
@@ -106,6 +104,8 @@ class TextEditor extends Model
|
||||
throw new Error("Must pass a project parameter when constructing TextEditors") unless @project?
|
||||
throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert?
|
||||
|
||||
@firstVisibleScreenRow ?= 0
|
||||
@firstVisibleScreenColumn ?= 0
|
||||
@emitter = new Emitter
|
||||
@disposables = new CompositeDisposable
|
||||
@cursors = []
|
||||
@@ -146,8 +146,8 @@ class TextEditor extends Model
|
||||
deserializer: 'TextEditor'
|
||||
id: @id
|
||||
softTabs: @softTabs
|
||||
scrollRow: @getScrollRow()
|
||||
scrollColumn: @getScrollColumn()
|
||||
firstVisibleScreenRow: @getFirstVisibleScreenRow()
|
||||
firstVisibleScreenColumn: @getFirstVisibleScreenColumn()
|
||||
displayBuffer: @displayBuffer.serialize()
|
||||
selectionsMarkerLayerId: @selectionsMarkerLayer.id
|
||||
|
||||
@@ -453,6 +453,9 @@ class TextEditor extends Model
|
||||
onDidChangeCharacterWidths: (callback) ->
|
||||
@displayBuffer.onDidChangeCharacterWidths(callback)
|
||||
|
||||
onDidChangeFirstVisibleScreenRow: (callback, fromView) ->
|
||||
@emitter.on 'did-change-first-visible-screen-row', callback
|
||||
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollTop instead.")
|
||||
|
||||
@@ -3130,14 +3133,6 @@ class TextEditor extends Model
|
||||
@placeholderText = placeholderText
|
||||
@emitter.emit 'did-change-placeholder-text', @placeholderText
|
||||
|
||||
getFirstVisibleScreenRow: ->
|
||||
deprecate("This is now a view method. Call TextEditorElement::getFirstVisibleScreenRow instead.")
|
||||
@viewRegistry.getView(this).getVisibleRowRange()[0]
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getLastVisibleScreenRow instead.")
|
||||
@viewRegistry.getView(this).getVisibleRowRange()[1]
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead")
|
||||
@viewRegistry.getView(this).pixelPositionForBufferPosition(bufferPosition)
|
||||
@@ -3192,11 +3187,40 @@ class TextEditor extends Model
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.")
|
||||
@displayBuffer.getWidth()
|
||||
|
||||
getScrollRow: -> @scrollRow
|
||||
setScrollRow: (@scrollRow) ->
|
||||
# Experimental: Scroll the editor such that the given screen row is at the
|
||||
# top of the visible area.
|
||||
setFirstVisibleScreenRow: (screenRow, fromView) ->
|
||||
unless fromView
|
||||
maxScreenRow = @getLineCount() - 1
|
||||
unless @config.get('editor.scrollPastEnd')
|
||||
height = @displayBuffer.getHeight()
|
||||
lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
|
||||
if height? and lineHeightInPixels?
|
||||
maxScreenRow -= Math.floor(height / lineHeightInPixels)
|
||||
screenRow = Math.max(Math.min(screenRow, maxScreenRow), 0)
|
||||
|
||||
getScrollColumn: -> @scrollColumn
|
||||
setScrollColumn: (@scrollColumn) ->
|
||||
unless screenRow is @firstVisibleScreenRow
|
||||
@firstVisibleScreenRow = screenRow
|
||||
@emitter.emit 'did-change-first-visible-screen-row', screenRow unless fromView
|
||||
|
||||
getFirstVisibleScreenRow: -> @firstVisibleScreenRow
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
height = @displayBuffer.getHeight()
|
||||
lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
|
||||
if height? and lineHeightInPixels?
|
||||
Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getLineCount() - 1)
|
||||
else
|
||||
null
|
||||
|
||||
getVisibleRowRange: ->
|
||||
if lastVisibleScreenRow = @getLastVisibleScreenRow()
|
||||
[@firstVisibleScreenRow, lastVisibleScreenRow]
|
||||
else
|
||||
null
|
||||
|
||||
setFirstVisibleScreenColumn: (@firstVisibleScreenColumn) ->
|
||||
getFirstVisibleScreenColumn: -> @firstVisibleScreenColumn
|
||||
|
||||
getScrollTop: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollTop instead.")
|
||||
@@ -3253,11 +3277,6 @@ class TextEditor extends Model
|
||||
|
||||
@viewRegistry.getView(this).getMaxScrollTop()
|
||||
|
||||
getVisibleRowRange: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getVisibleRowRange instead.")
|
||||
|
||||
@viewRegistry.getView(this).getVisibleRowRange()
|
||||
|
||||
intersectsVisibleRowRange: (startRow, endRow) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::intersectsVisibleRowRange instead.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user