Merge pull request #2497 from atom/cj-add-react-editor-shims

Add shims to the React View Editor
This commit is contained in:
Corey Johnson
2014-06-11 09:06:40 -07:00
14 changed files with 220 additions and 82 deletions

View File

@@ -230,8 +230,7 @@ class DisplayBuffer extends Model
@charWidthsByScope = {}
getScrollHeight: ->
unless @getLineHeightInPixels() > 0
throw new Error("You must assign lineHeightInPixels before calling ::getScrollHeight()")
return 0 unless @getLineHeightInPixels() > 0
@getLineCount() * @getLineHeightInPixels()
@@ -239,8 +238,7 @@ class DisplayBuffer extends Model
(@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth()
getVisibleRowRange: ->
unless @getLineHeightInPixels() > 0
throw new Error("You must assign a non-zero lineHeightInPixels before calling ::getVisibleRowRange()")
return [0, 0] unless @getLineHeightInPixels() > 0
heightInLines = Math.ceil(@getHeight() / @getLineHeightInPixels()) + 1
startRow = Math.floor(@getScrollTop() / @getLineHeightInPixels())

View File

@@ -37,6 +37,7 @@ EditorComponent = React.createClass
overflowChangedEventsPaused: false
overflowChangedWhilePaused: false
measureLineHeightAndDefaultCharWidthWhenShown: false
inputEnabled: true
render: ->
{focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state
@@ -165,7 +166,9 @@ EditorComponent = React.createClass
window.removeEventListener('resize', @onWindowResize)
componentWillUpdate: ->
@props.parentView.trigger 'cursor:moved' if @cursorsMoved
if @props.editor.isAlive()
@props.parentView.trigger 'cursor:moved' if @cursorsMoved
@props.parentView.trigger 'selection:changed' if @selectionChanged
componentDidUpdate: (prevProps, prevState) ->
@pendingChanges.length = 0
@@ -457,6 +460,8 @@ EditorComponent = React.createClass
scrollViewNode.scrollLeft = 0
onInput: (char, replaceLastCharacter) ->
return unless @inputEnabled
{editor} = @props
if replaceLastCharacter
@@ -531,6 +536,7 @@ EditorComponent = React.createClass
onCursorsMoved: ->
@cursorsMoved = true
@requestUpdate()
onDecorationChanged: ->
@decorationChangedImmediate ?= setImmediate =>
@@ -707,50 +713,21 @@ EditorComponent = React.createClass
show: ->
@setState(visible: true)
runScrollBenchmark: ->
unless process.env.NODE_ENV is 'production'
ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf'
ReactPerf.start()
node = @getDOMNode()
scroll = (delta, done) ->
dispatchMouseWheelEvent = ->
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -0, wheelDeltaY: -delta))
stopScrolling = ->
clearInterval(interval)
done?()
interval = setInterval(dispatchMouseWheelEvent, 10)
setTimeout(stopScrolling, 500)
console.timeline('scroll')
scroll 50, ->
scroll 100, ->
scroll 200, ->
scroll 400, ->
scroll 800, ->
scroll 1600, ->
console.timelineEnd('scroll')
unless process.env.NODE_ENV is 'production'
ReactPerf.stop()
console.log "Inclusive"
ReactPerf.printInclusive()
console.log "Exclusive"
ReactPerf.printExclusive()
console.log "Wasted"
ReactPerf.printWasted()
getFontSize: ->
@state.fontSize
setFontSize: (fontSize) ->
@setState({fontSize})
setLineHeight: (lineHeight) ->
@setState({lineHeight})
getFontFamily: ->
@state.fontFamily
setFontFamily: (fontFamily) ->
@setState({fontFamily})
setLineHeight: (lineHeight) ->
@setState({lineHeight})
setShowIndentGuide: (showIndentGuide) ->
@setState({showIndentGuide})
@@ -786,6 +763,48 @@ EditorComponent = React.createClass
left = clientX - scrollViewClientRect.left + editor.getScrollLeft()
{top, left}
getModel: ->
@props.editor
isInputEnabled: -> @inputEnabled
setInputEnabled: (@inputEnabled) -> @inputEnabled
updateParentViewFocusedClassIfNeeded: (prevState) ->
if prevState.focused isnt @state.focused
@props.parentView.toggleClass('is-focused', @props.focused)
runScrollBenchmark: ->
unless process.env.NODE_ENV is 'production'
ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf'
ReactPerf.start()
node = @getDOMNode()
scroll = (delta, done) ->
dispatchMouseWheelEvent = ->
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -0, wheelDeltaY: -delta))
stopScrolling = ->
clearInterval(interval)
done?()
interval = setInterval(dispatchMouseWheelEvent, 10)
setTimeout(stopScrolling, 500)
console.timeline('scroll')
scroll 50, ->
scroll 100, ->
scroll 200, ->
scroll 400, ->
scroll 800, ->
scroll 1600, ->
console.timelineEnd('scroll')
unless process.env.NODE_ENV is 'production'
ReactPerf.stop()
console.log "Inclusive"
ReactPerf.printInclusive()
console.log "Exclusive"
ReactPerf.printExclusive()
console.log "Wasted"
ReactPerf.printWasted()

View File

@@ -282,7 +282,7 @@ class EditorView extends View
@editor.moveCursorUp(@getPageRows())
@scrollTop(newScrollTop, adjustVerticalScrollbar: true)
# Public: Gets the number of actual page rows existing in an editor.
# Gets the number of actual page rows existing in an editor.
#
# Returns a {Number}.
getPageRows: ->

View File

@@ -62,7 +62,7 @@ GutterComponent = React.createClass
# since the real line numbers are absolutely positioned for performance reasons.
appendDummyLineNumber: ->
{maxLineNumberDigits} = @props
WrapperDiv.innerHTML = @buildLineNumberHTML(0, false, maxLineNumberDigits)
WrapperDiv.innerHTML = @buildLineNumberHTML(-1, false, maxLineNumberDigits)
@dummyLineNumberNode = WrapperDiv.children[0]
@refs.lineNumbers.getDOMNode().appendChild(@dummyLineNumberNode)
@@ -140,7 +140,7 @@ GutterComponent = React.createClass
if decorations?
for decoration in decorations
classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap
classes += 'line-number'
classes += "line-number line-number-#{bufferRow}"
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"

View File

@@ -212,7 +212,10 @@ class Pane extends Model
@destroyItem(item) for item in @getItems() when item isnt @activeItem
destroy: ->
super unless @container?.isAlive() and @container?.getPanes().length is 1
if @container?.isAlive() and @container.getPanes().length is 1
@destroyItems()
else
super
# Called by model superclass.
destroyed: ->
@@ -331,7 +334,7 @@ class Pane extends Model
if @parent.orientation isnt orientation
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]}))
newPane = new @constructor(extend({focused: true}, params))
newPane = new @constructor(params)
switch side
when 'before' then @parent.insertChildBefore(this, newPane)
when 'after' then @parent.insertChildAfter(this, newPane)

View File

@@ -23,6 +23,41 @@ class ReactEditorView extends View
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
Object.defineProperty @::, 'active', get: -> @is(@getPane().activeView)
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
afterAttach: (onDom) ->
return unless onDom
return if @attached
@attached = true
props = defaults({@editor, parentView: this}, @props)
@component = React.renderComponent(EditorComponent(props), @element)
node = @component.getDOMNode()
@scrollView = $(node).find('.scroll-view')
@underlayer = $(node).find('.selections').addClass('underlayer')
@overlayer = $(node).find('.lines').addClass('overlayer')
@hiddenInput = $(node).find('.hidden-input')
@gutter = $(node).find('.gutter')
@gutter.removeClassFromAllLines = (klass) =>
Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::removeDecorationFromBufferRow()` and related functions'
@gutter.find('.line-number').removeClass(klass)
@gutter.getLineNumberElement = (bufferRow) =>
@gutter.find("[data-buffer-row='#{bufferRow}']")
@gutter.addClassToLine = (bufferRow, klass) =>
Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::addDecorationToBufferRow()` and related functions'
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
lines.addClass(klass)
lines.length > 0
@focus() if @focusOnAttach
@trigger 'editor:attached', [this]
scrollTop: (scrollTop) ->
if scrollTop?
@@ -36,36 +71,21 @@ class ReactEditorView extends View
else
@editor.getScrollLeft()
scrollToBottom: ->
@editor.setScrollBottom(Infinity)
scrollToScreenPosition: (screenPosition) ->
@editor.scrollToScreenPosition(screenPosition)
scrollToBufferPosition: (bufferPosition) ->
@editor.scrollToBufferPosition(bufferPosition)
afterAttach: (onDom) ->
return unless onDom
@attached = true
props = defaults({@editor, parentView: this}, @props)
@component = React.renderComponent(EditorComponent(props), @element)
scrollToCursorPosition: ->
@editor.scrollToCursorPosition()
node = @component.getDOMNode()
@underlayer = $(node).find('.selections')
@gutter = $(node).find('.gutter')
@gutter.removeClassFromAllLines = (klass) =>
Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::removeDecorationFromBufferRow()` and related functions'
@gutter.find('.line-number').removeClass(klass)
@gutter.addClassToLine = (bufferRow, klass) =>
Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::addDecorationToBufferRow()` and related functions'
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
lines.addClass(klass)
lines.length > 0
@focus() if @focusOnAttach
@trigger 'editor:attached', [this]
scrollToPixelPosition: (pixelPosition) ->
screenPosition = screenPositionForPixelPosition(pixelPosition)
@editor.scrollToScreenPosition(screenPosition)
pixelPositionForBufferPosition: (bufferPosition) ->
@editor.pixelPositionForBufferPosition(bufferPosition)
@@ -83,6 +103,26 @@ class ReactEditorView extends View
@attached = false
@trigger 'editor:detached', this
# Public: Split the editor view left.
splitLeft: ->
pane = @getPane()
pane?.splitLeft(pane?.copyActiveItem()).activeView
# Public: Split the editor view right.
splitRight: ->
pane = @getPane()
pane?.splitRight(pane?.copyActiveItem()).activeView
# Public: Split the editor view up.
splitUp: ->
pane = @getPane()
pane?.splitUp(pane?.copyActiveItem()).activeView
# Public: Split the editor view down.
splitDown: ->
pane = @getPane()
pane?.splitDown(pane?.copyActiveItem()).activeView
getPane: ->
@closest('.pane').view()
@@ -94,10 +134,77 @@ class ReactEditorView extends View
hide: ->
super
@component.hide()
@component?.hide()
show: ->
super
@component.show()
@component?.show()
pageDown: ->
@editor.pageDown()
pageUp: ->
@editor.pageUp()
getModel: ->
@component?.getModel()
getFirstVisibleScreenRow: ->
@editor.getVisibleRowRange()[0]
getLastVisibleScreenRow: ->
@editor.getVisibleRowRange()[1]
getFontFamily: ->
@component?.getFontFamily()
setFontFamily: (fontFamily)->
@component?.setFontFamily(fontFamily)
getFontSize: ->
@component?.getFontSize()
setFontSize: (fontSize)->
@component?.setFontSize(fontSize)
setWidthInChars: (widthInChars) ->
@component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px'
setLineHeight: (lineHeight) ->
@component.setLineHeight(lineHeight)
setShowIndentGuide: (showIndentGuide) ->
@component.setShowIndentGuide(showIndentGuide)
setSoftWrap: (softWrap) ->
@editor.setSoftWrap(softWrap)
setShowInvisibles: (showInvisibles) ->
@component.setShowInvisibles(showInvisibles)
toggleSoftWrap: ->
@editor.toggleSoftWrap()
toggleSoftTabs: ->
@editor.toggleSoftTabs()
getText: ->
@editor.getText()
setText: (text) ->
@editor.setText(text)
insertText: (text) ->
@editor.insertText(text)
isInputEnabled: ->
@component.isInputEnabled()
setInputEnabled: (inputEnabled) ->
@component.setInputEnabled(inputEnabled)
requestDisplayUpdate: -> # No-op shim for find-and-replace
updateDisplay: -> # No-op shim for package specs
redraw: -> # No-op shim