Autoscroll the editor as a quadratic function of mouse cursor overshoot

This was a spike and still needs tests, but it’s going to be way better.
This commit is contained in:
Nathan Sobo
2015-08-27 18:57:06 -06:00
parent b2e8b05cbe
commit 567199a3d2
3 changed files with 62 additions and 32 deletions

View File

@@ -190,7 +190,7 @@ class Selection extends Model
# position.
#
# * `position` An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position) ->
selectToScreenPosition: (position, options) ->
position = Point.fromObject(position)
@modifySelection =>
@@ -200,12 +200,12 @@ class Selection extends Model
else
@marker.setScreenRange([@initialScreenRange.start, position], reversed: false)
else
@cursor.setScreenPosition(position)
@cursor.setScreenPosition(position, options)
if @linewise
@expandOverLine()
@expandOverLine(options)
else if @wordwise
@expandOverWord()
@expandOverWord(options)
# Public: Selects the text from the current cursor position to a given buffer
# position.
@@ -311,28 +311,28 @@ class Selection extends Model
# Public: Modifies the selection to encompass the current word.
#
# Returns a {Range}.
selectWord: ->
options = {}
selectWord: (options={}) ->
options.wordRegex = /[\t ]*/ if @cursor.isSurroundedByWhitespace()
if @cursor.isBetweenWordAndNonWord()
options.includeNonWordCharacters = false
@setBufferRange(@cursor.getCurrentWordBufferRange(options))
@setBufferRange(@cursor.getCurrentWordBufferRange(options), options)
@wordwise = true
@initialScreenRange = @getScreenRange()
# Public: Expands the newest selection to include the entire word on which
# the cursors rests.
expandOverWord: ->
expandOverWord: (options) ->
@setBufferRange(@getBufferRange().union(@cursor.getCurrentWordBufferRange()), autoscroll: false)
@cursor.autoscroll()
@cursor.autoscroll() if options?.autoscroll ? true
# Public: Selects an entire line in the buffer.
#
# * `row` The line {Number} to select (default: the row of the cursor).
selectLine: (row=@cursor.getBufferPosition().row) ->
selectLine: (row, options) ->
row ?= @cursor.getBufferPosition().row
range = @editor.bufferRangeForBufferRow(row, includeNewline: true)
@setBufferRange(@getBufferRange().union(range), autoscroll: true)
@setBufferRange(@getBufferRange().union(range), options)
@linewise = true
@wordwise = false
@initialScreenRange = @getScreenRange()
@@ -341,10 +341,10 @@ class Selection extends Model
# the cursor currently rests.
#
# It also includes the newline character.
expandOverLine: ->
expandOverLine: (options) ->
range = @getBufferRange().union(@cursor.getCurrentLineBufferRange(includeNewline: true))
@setBufferRange(range, autoscroll: false)
@cursor.autoscroll()
@cursor.autoscroll() if options?.autoscroll ? true
###
Section: Modifying the selected text

View File

@@ -395,16 +395,16 @@ class TextEditorComponent
if cursorAtScreenPosition and @editor.hasMultipleCursors()
cursorAtScreenPosition.destroy()
else
@editor.addCursorAtScreenPosition(screenPosition)
@editor.addCursorAtScreenPosition(screenPosition, autoscroll: false)
else
@editor.setCursorScreenPosition(screenPosition)
@editor.setCursorScreenPosition(screenPosition, autoscroll: false)
when 2
@editor.getLastSelection().selectWord()
@editor.getLastSelection().selectWord(autoscroll: false)
when 3
@editor.getLastSelection().selectLine()
@editor.getLastSelection().selectLine(null, autoscroll: false)
@handleDragUntilMouseUp (screenPosition) =>
@editor.selectToScreenPosition(screenPosition, true)
@editor.selectToScreenPosition(screenPosition, suppressSelectionMerge: true, autoscroll: false)
onLineNumberGutterMouseDown: (event) =>
return unless event.button is 0 # only handle the left mouse button
@@ -422,14 +422,14 @@ class TextEditorComponent
clickedScreenRow = @screenPositionForMouseEvent(event).row
clickedBufferRow = @editor.bufferRowForScreenRow(clickedScreenRow)
initialScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]])
@editor.setSelectedScreenRange(initialScreenRange, preserveFolds: true)
@editor.setSelectedScreenRange(initialScreenRange, preserveFolds: true, autoscroll: false)
@handleGutterDrag(initialScreenRange)
onGutterMetaClick: (event) =>
clickedScreenRow = @screenPositionForMouseEvent(event).row
clickedBufferRow = @editor.bufferRowForScreenRow(clickedScreenRow)
initialScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]])
@editor.addSelectionForScreenRange(initialScreenRange, preserveFolds: true)
@editor.addSelectionForScreenRange(initialScreenRange, preserveFolds: true, autoscroll: false)
@handleGutterDrag(initialScreenRange)
onGutterShiftClick: (event) =>
@@ -439,9 +439,9 @@ class TextEditorComponent
clickedLineScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]])
if clickedScreenRow < tailScreenPosition.row
@editor.selectToScreenPosition(clickedLineScreenRange.start, true)
@editor.selectToScreenPosition(clickedLineScreenRange.start, suppressSelectionMerge: true, autoscroll: false)
else
@editor.selectToScreenPosition(clickedLineScreenRange.end, true)
@editor.selectToScreenPosition(clickedLineScreenRange.end, suppressSelectionMerge: true, autoscroll: false)
@handleGutterDrag(new Range(tailScreenPosition, tailScreenPosition))
@@ -456,7 +456,6 @@ class TextEditorComponent
endPosition = [dragRow + 1, 0]
screenRange = new Range(endPosition, endPosition).union(initialRange)
@editor.getLastSelection().setScreenRange(screenRange, reversed: false, autoscroll: false, preserveFolds: true)
@editor.getLastCursor().autoscroll()
onStylesheetsChanged: (styleElement) =>
return unless @performedInitialMeasurement
@@ -512,7 +511,9 @@ class TextEditorComponent
animationLoop = =>
@requestAnimationFrame =>
if dragging and @mounted
screenPosition = @screenPositionForMouseEvent(lastMousePosition)
linesClientRect = @linesComponent.getDomNode().getBoundingClientRect()
autoscroll(lastMousePosition, linesClientRect)
screenPosition = @screenPositionForMouseEvent(lastMousePosition, linesClientRect)
dragHandler(screenPosition)
animationLoop()
else if not @mounted
@@ -543,6 +544,32 @@ class TextEditorComponent
window.removeEventListener('mouseup', onMouseUp)
willInsertTextSubscription.dispose()
autoscroll = (mouseClientPosition) =>
editorClientRect = @domNode.getBoundingClientRect()
if mouseClientPosition.clientY < editorClientRect.top
mouseYDelta = editorClientRect.top - mouseClientPosition.clientY
yDirection = -1
else if mouseClientPosition.clientY > editorClientRect.bottom
mouseYDelta = mouseClientPosition.clientY - editorClientRect.bottom
yDirection = 1
if mouseClientPosition.clientX < editorClientRect.left
mouseXDelta = editorClientRect.left - mouseClientPosition.clientX
xDirection = -1
else if mouseClientPosition.clientX > editorClientRect.right
mouseXDelta = mouseClientPosition.clientX - editorClientRect.right
xDirection = 1
if mouseYDelta?
@presenter.setScrollTop(@presenter.getScrollTop() + yDirection * scaleScrollDelta(mouseYDelta))
if mouseXDelta?
@presenter.setScrollLeft(@presenter.getScrollLeft() + xDirection * scaleScrollDelta(mouseXDelta))
scaleScrollDelta = (scrollDelta) ->
Math.pow(scrollDelta / 2, 3) / 280
pasteSelectionClipboard = (event) =>
if event?.which is 2 and process.platform is 'linux'
if selection = require('./safe-clipboard').readText('selection')
@@ -748,17 +775,20 @@ class TextEditorComponent
if scrollSensitivity = parseInt(scrollSensitivity)
@scrollSensitivity = Math.abs(scrollSensitivity) / 100
screenPositionForMouseEvent: (event) ->
pixelPosition = @pixelPositionForMouseEvent(event)
screenPositionForMouseEvent: (event, linesClientRect) ->
pixelPosition = @pixelPositionForMouseEvent(event, linesClientRect)
@editor.screenPositionForPixelPosition(pixelPosition)
pixelPositionForMouseEvent: (event) ->
pixelPositionForMouseEvent: (event, linesClientRect) ->
{clientX, clientY} = event
linesClientRect = @linesComponent.getDomNode().getBoundingClientRect()
linesClientRect ?= @linesComponent.getDomNode().getBoundingClientRect()
top = clientY - linesClientRect.top + @presenter.scrollTop
left = clientX - linesClientRect.left + @presenter.scrollLeft
{top, left}
bottom = linesClientRect.top + @presenter.scrollTop + linesClientRect.height - clientY
right = linesClientRect.left + @presenter.scrollLeft + linesClientRect.width - clientX
{top, left, bottom, right}
getModel: ->
@editor

View File

@@ -1943,10 +1943,10 @@ class TextEditor extends Model
# This method may merge selections that end up intesecting.
#
# * `position` An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position, suppressMerge) ->
selectToScreenPosition: (position, options) ->
lastSelection = @getLastSelection()
lastSelection.selectToScreenPosition(position)
unless suppressMerge
lastSelection.selectToScreenPosition(position, options)
unless options?.suppressSelectionMerge
@mergeIntersectingSelections(reversed: lastSelection.isReversed())
# Essential: Move the cursor of each selection one character upward while