From 70e5880b1d02be34dfab04e17dd2aaaecbbaaddd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 1 Apr 2014 15:06:38 -0600 Subject: [PATCH] Start on cursor rendering --- spec/editor-component-spec.coffee | 21 ++++++++--- src/editor-component.coffee | 62 ++++++++++++++++++++++++------- static/editor.less | 10 +++++ 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index c477801ec..19e639d1d 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2,7 +2,7 @@ EditorComponent = require '../src/editor-component' describe "EditorComponent", -> - [editor, component, node, lineHeight] = [] + [editor, component, node, lineHeight, charWidth] = [] beforeEach -> editor = atom.project.openSync('sample.js') @@ -10,10 +10,9 @@ describe "EditorComponent", -> component = React.renderComponent(EditorComponent({editor}), container) node = component.getDOMNode() - fontSize = 20 - lineHeight = 1.3 * fontSize node.style.lineHeight = 1.3 - node.style.fontSize = fontSize + 'px' + node.style.fontSize = '20px' + {lineHeight, charWidth} = component.measureLineDimensions() it "renders only the currently-visible lines", -> node.style.height = 4.5 * lineHeight + 'px' @@ -28,7 +27,7 @@ describe "EditorComponent", -> spyOn(window, 'requestAnimationFrame').andCallFake (fn) -> fn() component.onVerticalScroll() - expect(node.querySelector('.lines').style['-webkit-transform']).toBe "translateY(#{-2.5 * lineHeight}px)" + expect(node.querySelector('.scrollable-content').style['-webkit-transform']).toBe "translateY(#{-2.5 * lineHeight}px)" lines = node.querySelectorAll('.line') expect(lines.length).toBe 5 @@ -38,3 +37,15 @@ describe "EditorComponent", -> spacers = node.querySelectorAll('.spacer') expect(spacers[0].offsetHeight).toBe 2 * lineHeight expect(spacers[1].offsetHeight).toBe (editor.getScreenLineCount() - 7) * lineHeight + + it "renders the currently visible selections", -> + editor.setCursorScreenPosition([0, 5]) + + node.style.height = 4.5 * lineHeight + 'px' + component.updateAllDimensions() + + cursorNodes = node.querySelectorAll('.cursor') + expect(cursorNodes[0].offsetHeight).toBe lineHeight + expect(cursorNodes[0].offsetWidth).toBe charWidth + expect(cursorNodes[0].offsetTop).toBe 0 + expect(cursorNodes[0].offsetLeft).toBe 5 * charWidth diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 0c39f7f5d..19bfead6f 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -10,22 +10,37 @@ module.exports = React.createClass pendingScrollTop: null + statics: {DummyLineNode} + render: -> div className: 'editor', div className: 'scroll-view', ref: 'scrollView', - div className: 'overlayer', - InputComponent ref: 'hiddenInput', className: 'hidden-input', onInput: @onInput - @renderVisibleLines() + InputComponent ref: 'hiddenInput', className: 'hidden-input', onInput: @onInput + @renderScrollableContent() div className: 'vertical-scrollbar', ref: 'verticalScrollbar', onScroll: @onVerticalScroll, div outlet: 'verticalScrollbarContent', style: {height: @getScrollHeight()} + renderScrollableContent: -> + height = @props.editor.getScreenLineCount() * @state.lineHeight + WebkitTransform = "translateY(#{-@state.scrollTop}px)" + + div className: 'scrollable-content', style: {height, WebkitTransform}, + @renderOverlayer() + @renderVisibleLines() + + renderOverlayer: -> + {lineHeight, charWidth} = @state + + div className: 'overlayer', + for selection in @props.editor.getSelections() + SelectionComponent({selection, lineHeight, charWidth}) + renderVisibleLines: -> [startRow, endRow] = @getVisibleRowRange() precedingHeight = startRow * @state.lineHeight - lineCount = @props.editor.getScreenLineCount() - followingHeight = (lineCount - endRow) * @state.lineHeight + followingHeight = (@props.editor.getScreenLineCount() - endRow) * @state.lineHeight - div className: 'lines', ref: 'lines', style: {WebkitTransform: "translateY(#{-@state.scrollTop}px)"}, [ + div className: 'lines', ref: 'lines', [ div className: 'spacer', key: 'top-spacer', style: {height: precedingHeight} (for tokenizedLine in @props.editor.linesForScreenRows(startRow, endRow - 1) LineComponent({tokenizedLine, key: tokenizedLine.id}))... @@ -62,8 +77,6 @@ React.createClass event.preventDefault() onInput: (char, replaceLastChar) -> - console.log char, replaceLastChar - @props.editor.insertText(char) onScreenLinesChanged: ({start, end}) -> @@ -82,21 +95,21 @@ React.createClass @props.editor.getLineCount() * @state.lineHeight updateAllDimensions: -> - lineHeight = @measureLineHeight() {height, width} = @measureScrollViewDimensions() - - @setState({lineHeight, height, width}) + {lineHeight, charWidth} = @measureLineDimensions() + @setState({height, width, lineHeight, charWidth}) measureScrollViewDimensions: -> scrollViewNode = @refs.scrollView.getDOMNode() {height: scrollViewNode.clientHeight, width: scrollViewNode.clientWidth} - measureLineHeight: -> + measureLineDimensions: -> linesNode = @refs.lines.getDOMNode() linesNode.appendChild(DummyLineNode) lineHeight = DummyLineNode.getBoundingClientRect().height + charWidth = DummyLineNode.firstChild.getBoundingClientRect().width linesNode.removeChild(DummyLineNode) - lineHeight + {lineHeight, charWidth} LineComponent = React.createClass render: -> @@ -151,3 +164,26 @@ InputComponent = React.createClass focus: -> @getDOMNode().focus() + +SelectionComponent = React.createClass + render: -> + console.log "render selection component" + + {selection, lineHeight, charWidth} = @props + {cursor} = selection + div className: 'selection', + CursorComponent({cursor, lineHeight, charWidth}) + +CursorComponent = React.createClass + render: -> + {cursor, lineHeight, charWidth} = @props + {row, column} = cursor.getScreenPosition() + + console.log "char width", charWidth + + div className: 'cursor', style: { + height: lineHeight, + width: charWidth + top: row * lineHeight + left: column * charWidth + } diff --git a/static/editor.less b/static/editor.less index e0ef83686..ea5c932fd 100644 --- a/static/editor.less +++ b/static/editor.less @@ -200,3 +200,13 @@ color: @text-color-subtle; } } + +.react-wrapper > .editor { + .scrollable-content { + position: relative; + } + + .cursor { + visibility: visible; + } +}