From 4218e0a037f522757029588d7c7aa2714c0f83be Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 20 Jun 2014 14:42:30 -0600 Subject: [PATCH 1/3] Render highlights on their own layer to avoid GPU artifacts Previously, when the last highlight div was removed from the lines layer, chunks of the lines would sometimes disappear. Since we've discovered that giving the lines an opaque background doesn't help with subpixel anti-aliasing anyway, I've found that rendering highlights on their own layer behind the lines and making the lines layer transparent avoids the arficacts. --- src/editor-component.coffee | 7 ++++++- src/editor.coffee | 3 ++- src/highlights-component.coffee | 16 ++++++++++++++-- src/lines-component.coffee | 11 +++-------- static/editor.less | 2 +- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 7f475fb1b..7cdcbf713 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -8,6 +8,7 @@ GutterComponent = require './gutter-component' InputComponent = require './input-component' CursorsComponent = require './cursors-component' LinesComponent = require './lines-component' +HighlightsComponent = require './highlights-component' ScrollbarComponent = require './scrollbar-component' ScrollbarCornerComponent = require './scrollbar-corner-component' SubscriberMixin = require './subscriber-mixin' @@ -103,7 +104,11 @@ EditorComponent = React.createClass editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations, showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles, - visible, scrollViewHeight, @scopedCharacterWidthsChangeCount + visible, scrollViewHeight + } + HighlightsComponent { + editor, scrollTop, scrollLeft, scrollHeight, scrollWidth, highlightDecorations, lineHeightInPixels, + defaultCharWidth, @scopedCharacterWidthsChangeCount } ScrollbarComponent diff --git a/src/editor.coffee b/src/editor.coffee index df0d69270..cba409213 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -443,7 +443,8 @@ class Editor extends Model getText: -> @buffer.getText() # Public: Replaces the entire contents of the buffer with the given {String}. - setText: (text) -> @buffer.setText(text) + setText: (text) -> + @buffer.setText(text) # Get the text in the given {Range}. # diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 292e94892..6a7200e24 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -8,7 +8,16 @@ HighlightsComponent = React.createClass displayName: 'HighlightsComponent' render: -> - div className: 'highlights', @renderHighlights() + if @isMounted() + {scrollTop, scrollLeft, scrollHeight, scrollWidth} = @props + style = + height: scrollHeight + width: scrollWidth + WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)" + + div {className: 'highlights', style}, + @renderHighlights() if @isMounted() + renderHighlights: -> {editor, highlightDecorations, lineHeightInPixels} = @props @@ -21,4 +30,7 @@ HighlightsComponent = React.createClass highlightComponents shouldComponentUpdate: (newProps) -> - not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount') + not isEqualForProperties(newProps, @props, + 'scrollTop', 'scrollLeft', 'highlightDecorations', 'lineHeightInPixels', + 'defaultCharWidth', 'scopedCharacterWidthsChangeCount' + ) diff --git a/src/lines-component.coffee b/src/lines-component.coffee index afbeb1b57..92b9b7f89 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -4,8 +4,6 @@ React = require 'react-atom-fork' {debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus' {$$} = require 'space-pen' -HighlightsComponent = require './highlights-component' - DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0] AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT} WrapperDiv = document.createElement('div') @@ -17,16 +15,13 @@ LinesComponent = React.createClass render: -> if @isMounted() {editor, highlightDecorations, scrollTop, scrollLeft, scrollHeight, scrollWidth} = @props - {lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props + {lineHeightInPixels, defaultCharWidth, scrollViewHeight} = @props style = height: Math.max(scrollHeight, scrollViewHeight) width: scrollWidth WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)" - # The lines div must have the 'editor-colors' class so it has an opaque - # background to avoid sub-pixel anti-aliasing problems on the GPU - div {className: 'lines editor-colors', style}, - HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount}) if @isMounted() + div {className: 'lines', style} componentWillMount: -> @measuredLines = new WeakSet @@ -39,7 +34,7 @@ LinesComponent = React.createClass return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible', - 'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount' + 'scrollViewHeight', 'mouseWheelScreenRow' ) {renderedRowRange, pendingChanges} = newProps diff --git a/static/editor.less b/static/editor.less index 990097d06..acede6075 100644 --- a/static/editor.less +++ b/static/editor.less @@ -12,7 +12,7 @@ z-index: -2; } - .lines { + .lines, .highlights { min-width: 100%; } From d839ea9aa534d3ec947e230c4998ca2f4faa9956 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 20 Jun 2014 16:07:19 -0600 Subject: [PATCH 2/3] Don't render an opaque background behind line numbers It doesn't help subpixel anti-aliasing like I thought, so screw it. --- spec/editor-component-spec.coffee | 6 ------ src/editor-component.coffee | 4 ++-- src/gutter-component.coffee | 10 +++------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index d47abdb00..6382a1474 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -462,12 +462,6 @@ describe "EditorComponent", -> expect(component.lineNumberNodeForScreenRow(9).textContent).toBe "10" expect(gutterNode.offsetWidth).toBe initialGutterWidth - it "renders the .line-numbers div at the full height of the editor even if it's taller than its content", -> - node.style.height = node.offsetHeight + 100 + 'px' - component.measureScrollView() - nextTick() - expect(node.querySelector('.line-numbers').offsetHeight).toBe node.offsetHeight - describe "fold decorations", -> describe "rendering fold decorations", -> it "adds the foldable class to line numbers when the line is foldable", -> diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 7cdcbf713..4aa79bafc 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -83,8 +83,8 @@ EditorComponent = React.createClass div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1, GutterComponent { ref: 'gutter', onMouseDown: @onGutterMouseDown, onWidthChanged: @onGutterWidthChanged, - lineDecorations, defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight, - scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow + lineDecorations, defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, + scrollTop, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow } div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown, diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index 2555c7ebd..a7e82180d 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -15,13 +15,10 @@ GutterComponent = React.createClass measuredWidth: null render: -> - {scrollHeight, scrollViewHeight, scrollTop, onMouseDown} = @props + {scrollTop, onMouseDown} = @props div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown, - # The line-numbers div must have the 'editor-colors' class so it has an - # opaque background to avoid sub-pixel anti-aliasing problems on the GPU - div className: 'gutter line-numbers editor-colors', ref: 'lineNumbers', style: - height: Math.max(scrollHeight, scrollViewHeight) + div className: 'line-numbers', ref: 'lineNumbers', style: WebkitTransform: "translate3d(0px, #{-scrollTop}px, 0px)" componentWillMount: -> @@ -38,8 +35,7 @@ GutterComponent = React.createClass # visible row range. shouldComponentUpdate: (newProps) -> return true unless isEqualForProperties(newProps, @props, - 'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow', 'lineDecorations', - 'scrollViewHeight' + 'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow', 'lineDecorations' ) {renderedRowRange, pendingChanges, lineDecorations} = newProps From df8d014e3cc655f0f2c225e567f4dae6178aef34 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 20 Jun 2014 16:33:08 -0600 Subject: [PATCH 3/3] Add a dedicated underlayer to avoid GPU artifacts on wrap guide etc Trying to make the .highlights layer double as the .underlayer was causing GPU artifacts on the wrap guide when the last highlight was removed. This puts it in its own layer to avoid that. --- src/editor-component.coffee | 4 ++++ src/react-editor-view.coffee | 2 +- src/underlayer-component.coffee | 20 ++++++++++++++++++++ static/editor.less | 7 ++++--- 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 src/underlayer-component.coffee diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 4aa79bafc..e8b399da5 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -9,6 +9,7 @@ InputComponent = require './input-component' CursorsComponent = require './cursors-component' LinesComponent = require './lines-component' HighlightsComponent = require './highlights-component' +UnderlayerComponent = require './underlayer-component' ScrollbarComponent = require './scrollbar-component' ScrollbarCornerComponent = require './scrollbar-corner-component' SubscriberMixin = require './subscriber-mixin' @@ -110,6 +111,9 @@ EditorComponent = React.createClass editor, scrollTop, scrollLeft, scrollHeight, scrollWidth, highlightDecorations, lineHeightInPixels, defaultCharWidth, @scopedCharacterWidthsChangeCount } + UnderlayerComponent { + scrollTop, scrollLeft, scrollHeight, scrollWidth + } ScrollbarComponent ref: 'verticalScrollbar' diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index a363026e2..8c0b961d0 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -34,7 +34,7 @@ class ReactEditorView extends View node = @component.getDOMNode() @scrollView = $(node).find('.scroll-view') - @underlayer = $(node).find('.highlights').addClass('underlayer') + @underlayer = $(node).find('.underlayer') @overlayer = $(node).find('.lines').addClass('overlayer') @hiddenInput = $(node).find('.hidden-input') diff --git a/src/underlayer-component.coffee b/src/underlayer-component.coffee new file mode 100644 index 000000000..cf106587a --- /dev/null +++ b/src/underlayer-component.coffee @@ -0,0 +1,20 @@ +React = require 'react-atom-fork' +{div} = require 'reactionary-atom-fork' +{isEqualForProperties} = require 'underscore-plus' + +module.exports = +UnderlayerComponent = React.createClass + displayName: 'UnderlayerComponent' + + render: -> + if @isMounted() + {scrollTop, scrollLeft, scrollHeight, scrollWidth} = @props + style = + height: scrollHeight + width: scrollWidth + WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)" + + div {className: 'underlayer', style} + + shouldComponentUpdate: (newProps) -> + not isEqualForProperties(@props, newProps, 'scrollTop', 'scrollLeft', 'scrollHeight', 'scrollWidth') diff --git a/static/editor.less b/static/editor.less index acede6075..e4639327d 100644 --- a/static/editor.less +++ b/static/editor.less @@ -6,13 +6,14 @@ .underlayer { position: absolute; top: 0; - bottom: 0; left: 0; - right: 0; + } + + .highlights { z-index: -2; } - .lines, .highlights { + .lines, .highlights, .underlayer { min-width: 100%; }