Merge pull request #4144 from atom/bo-overlay-decoration

Overlay decorations
This commit is contained in:
Ben Ogle
2014-11-12 15:02:40 -08:00
7 changed files with 301 additions and 6 deletions

View File

@@ -17,7 +17,7 @@ class Cursor extends Model
visible: true
needsAutoscroll: null
# Instantiated by an {TextEditor}
# Instantiated by a {TextEditor}
constructor: ({@editor, @marker, id}) ->
@emitter = new Emitter
@@ -171,6 +171,10 @@ class Cursor extends Model
Section: Cursor Position Details
###
# Public: Returns the underlying {Marker} for the cursor.
# Useful with overlay {Decoration}s.
getMarker: -> @marker
# Public: Identifies if the cursor is surrounded by whitespace.
#
# "Surrounded" here means that the character directly before and after the

View File

@@ -7,6 +7,7 @@ React = require 'react-atom-fork'
Decoration = require './decoration'
CursorsComponent = require './cursors-component'
HighlightsComponent = require './highlights-component'
OverlayManager = require './overlay-manager'
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
@@ -20,7 +21,7 @@ LinesComponent = React.createClass
{performedInitialMeasurement, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
if performedInitialMeasurement
{editor, highlightDecorations, scrollHeight, scrollWidth, placeholderText, backgroundColor} = @props
{editor, overlayDecorations, highlightDecorations, scrollHeight, scrollWidth, placeholderText, backgroundColor} = @props
{lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props
{scrollTop, scrollLeft, cursorPixelRects, mini} = @props
style =
@@ -63,10 +64,17 @@ LinesComponent = React.createClass
insertionPoint.setAttribute('select', '.overlayer')
@getDOMNode().appendChild(insertionPoint)
insertionPoint = document.createElement('content')
insertionPoint.setAttribute('select', 'atom-overlay')
@overlayManager = new OverlayManager(@props.hostElement)
@getDOMNode().appendChild(insertionPoint)
else
@overlayManager = new OverlayManager(@getDOMNode())
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'visible',
'overlayDecorations', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'visible',
'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount', 'lineWidth', 'useHardwareAcceleration',
'placeholderText', 'performedInitialMeasurement', 'backgroundColor', 'cursorPixelRects'
)
@@ -92,6 +100,8 @@ LinesComponent = React.createClass
@updateLines(@props.lineWidth isnt prevProps.lineWidth)
@measureCharactersInNewLines() if visible and not scrollingVertically
@overlayManager?.render(@props)
clearScreenRowCaches: ->
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}

View File

@@ -0,0 +1,43 @@
module.exports =
class OverlayManager
constructor: (@container) ->
@overlays = {}
render: (props) ->
{editor, overlayDecorations, lineHeightInPixels} = props
existingDecorations = null
for markerId, {isMarkerReversed, headPixelPosition, decorations} of overlayDecorations
for decoration in decorations
@renderOverlay(editor, decoration, headPixelPosition, lineHeightInPixels)
existingDecorations ?= {}
existingDecorations[decoration.id] = true
for id, overlay of @overlays
unless existingDecorations? and id of existingDecorations
@container.removeChild(overlay)
delete @overlays[id]
return
renderOverlay: (editor, decoration, pixelPosition, lineHeightInPixels) ->
item = atom.views.getView(decoration.item)
unless overlay = @overlays[decoration.id]
overlay = @overlays[decoration.id] = document.createElement('atom-overlay')
overlay.appendChild(item)
@container.appendChild(overlay)
itemWidth = item.offsetWidth
itemHeight = item.offsetHeight
left = pixelPosition.left
if left + itemWidth - editor.getScrollLeft() > editor.getWidth() and left - itemWidth >= editor.getScrollLeft()
left -= itemWidth
top = pixelPosition.top + lineHeightInPixels
if top + itemHeight - editor.getScrollTop() > editor.getHeight() and top - itemHeight - lineHeightInPixels >= editor.getScrollTop()
top -= itemHeight + lineHeightInPixels
overlay.style.top = top + 'px'
overlay.style.left = left + 'px'

View File

@@ -50,7 +50,7 @@ TextEditorComponent = React.createClass
render: ->
{focused, showIndentGuide, showLineNumbers, visible} = @state
{editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
{editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay, hostElement} = @props
maxLineNumberDigits = editor.getLineCount().toString().length
hasSelection = editor.getLastSelection()? and !editor.getLastSelection().isEmpty()
style = {}
@@ -66,6 +66,7 @@ TextEditorComponent = React.createClass
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
highlightDecorations = @getHighlightDecorations(decorations)
overlayDecorations = @getOverlayDecorations(decorations)
lineDecorations = @getLineDecorations(decorations)
placeholderText = editor.getPlaceholderText() if editor.isEmpty()
visible = @isVisible()
@@ -110,7 +111,8 @@ TextEditorComponent = React.createClass
LinesComponent {
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, tokenizedLines, lineDecorations, highlightDecorations,
editor, lineHeightInPixels, defaultCharWidth, tokenizedLines,
lineDecorations, highlightDecorations, overlayDecorations, hostElement,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow,
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
@@ -351,7 +353,23 @@ TextEditorComponent = React.createClass
endPixelPosition: editor.pixelPositionForScreenPosition(screenRange.end)
decorations: []
filteredDecorations[markerId].decorations.push decorationParams
filteredDecorations
getOverlayDecorations: (decorationsByMarkerId) ->
{editor} = @props
filteredDecorations = {}
for markerId, decorations of decorationsByMarkerId
marker = editor.getMarker(markerId)
headBufferPosition = marker.getHeadBufferPosition()
if marker.isValid()
for decoration in decorations
if decoration.isType('overlay')
decorationParams = decoration.getProperties()
filteredDecorations[markerId] ?=
id: markerId
headPixelPosition: editor.pixelPositionForScreenPosition(headBufferPosition)
decorations: []
filteredDecorations[markerId].decorations.push decorationParams
filteredDecorations
observeEditor: ->

View File

@@ -77,7 +77,6 @@ class TextEditorView extends View
@scrollView = @root.find('.scroll-view')
if atom.config.get('editor.useShadowDOM')
@underlayer = $("<div class='underlayer'></div>").appendTo(this)
@overlayer = $("<div class='overlayer'></div>").appendTo(this)