diff --git a/src/overlay-manager.coffee b/src/overlay-manager.coffee index 0babb45ae..2ab7123ab 100644 --- a/src/overlay-manager.coffee +++ b/src/overlay-manager.coffee @@ -1,30 +1,86 @@ module.exports = class OverlayManager constructor: (@presenter, @container) -> - @overlayNodesById = {} + @overlaysById = {} render: (state) -> - for decorationId, {pixelPosition, item} of state.content.overlays - @renderOverlay(state, decorationId, item, pixelPosition) + editorDimensionsHaveChanged = !@editorDimensionsAreEqual(state) - for id, overlayNode of @overlayNodesById + for decorationId, overlay of state.content.overlays + overlayHasChanged = not @overlayStateIsEqual(decorationId, overlay) + if editorDimensionsHaveChanged or overlayHasChanged + @renderOverlay(state, decorationId, overlay) + @cacheOverlayState(decorationId, overlay) + + for id, {overlayNode} of @overlaysById unless state.content.overlays.hasOwnProperty(id) - delete @overlayNodesById[id] + delete @overlaysById[id] overlayNode.remove() - return + @cacheEditorDimensions(state) - renderOverlay: (state, decorationId, item, pixelPosition) -> + overlayStateIsEqual: (decorationId, overlay) -> + return false unless @overlaysById[decorationId]? + @overlaysById[decorationId].itemWidth is overlay.itemWidth and + @overlaysById[decorationId].itemHeight is overlay.itemHeight and + @overlaysById[decorationId].contentMargin is overlay.contentMargin and + @overlaysById[decorationId].pixelPosition?.top is overlay.pixelPosition?.top and + @overlaysById[decorationId].pixelPosition?.left is overlay.pixelPosition?.left + + cacheOverlayState: (decorationId, overlay) -> + return unless @overlaysById[decorationId]? + @overlaysById[decorationId].itemWidth = overlay.itemWidth + @overlaysById[decorationId].itemHeight = overlay.itemHeight + @overlaysById[decorationId].contentMargin = overlay.contentMargin + @overlaysById[decorationId].pixelPosition = overlay.pixelPosition + + cacheEditorDimensions: (state) -> + @cachedEditorDimensions = + lineHeight: @presenter.lineHeight + contentFrameWidth: @presenter.contentFrameWidth + editorTop: @presenter.boundingClientRect?.top + editorLeft: @presenter.boundingClientRect?.left + editorWidth: @presenter.boundingClientRect?.width + windowWidth: @presenter.windowWidth + windowHeight: @presenter.windowHeight + scrollTop: state.content.scrollTop + scrollLeft: state.content.scrollLeft + + editorDimensionsAreEqual: (state) -> + return false unless @cachedEditorDimensions? + @cachedEditorDimensions.lineHeight is @presenter.lineHeight and + @cachedEditorDimensions.contentFrameWidth is @presenter.contentFrameWidth and + @cachedEditorDimensions.editorTop is @presenter.boundingClientRect?.top and + @cachedEditorDimensions.editorLeft is @presenter.boundingClientRect?.left and + @cachedEditorDimensions.editorWidth is @presenter.boundingClientRect?.width and + @cachedEditorDimensions.windowWidth is @presenter.windowWidth and + @cachedEditorDimensions.windowHeight is @presenter.windowHeight and + @cachedEditorDimensions.scrollTop is state.content.scrollTop and + @cachedEditorDimensions.scrollLeft is state.content.scrollLeft + + measureOverlays: -> + for decorationId, {item} of @overlaysById + @measureOverlay(decorationId, item) + + measureOverlay: (decorationId, item) -> + contentMargin = parseInt(getComputedStyle(item)['margin-left']) ? 0 + @presenter.setOverlayDimensions(decorationId, item.offsetWidth, item.offsetHeight, contentMargin) + + renderOverlay: (state, decorationId, {item, pixelPosition}) -> item = atom.views.getView(item) - unless overlayNode = @overlayNodesById[decorationId] - overlayNode = @overlayNodesById[decorationId] = document.createElement('atom-overlay') + overlay = @overlaysById[decorationId] + unless overlayNode = overlay?.overlayNode + overlayNode = document.createElement('atom-overlay') overlayNode.appendChild(item) @container.appendChild(overlayNode) + @overlaysById[decorationId] = overlay = {overlayNode, item} - itemWidth = item.offsetWidth - itemHeight = item.offsetHeight - contentMargin = parseInt(getComputedStyle(item)['margin-left']) ? 0 + overlayDimensions = @presenter.getOverlayDimensions(decorationId) + unless overlayDimensions?.itemWidth? + @measureOverlay(decorationId, item) + overlayDimensions = @presenter.getOverlayDimensions(decorationId) + {itemWidth, itemHeight, contentMargin} = overlayDimensions {scrollTop, scrollLeft} = state.content editorBounds = @presenter.boundingClientRect diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 0de081d85..459d4368b 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -158,6 +158,7 @@ class TextEditorComponent readAfterUpdateSync: => @linesComponent.measureCharactersInNewLines() if @isVisible() and not @newState.content.scrollingVertically + @overlayManager?.measureOverlays() mountGutterComponent: -> @gutterComponent = new GutterComponent({@editor, onMouseDown: @onGutterMouseDown}) @@ -189,6 +190,8 @@ class TextEditorComponent else unless @updateRequested @updateRequested = true atom.views.updateDocument => + @editor.horribleUpdateMethod?() + @editor.horribleUpdateMethod = null @updateRequested = false @updateSync() if @editor.isAlive() atom.views.readDocument(@readAfterUpdateSync) @@ -568,6 +571,7 @@ class TextEditorComponent @sampleBackgroundColors() @measureDimensions() @sampleFontStyling() + @overlayManager?.measureOverlays() checkForVisibilityChange: -> if @isVisible() @@ -617,11 +621,7 @@ class TextEditorComponent measureWindowSize: -> return unless @mounted - - width = window.innerWidth - height = window.innerHeight - - @presenter.setWindowSize(width, height) + @presenter.setWindowSize(window.innerWidth, window.innerHeight) sampleFontStyling: => oldFontSize = @fontSize diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 81eb93a5b..649e8f290 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1012,6 +1012,20 @@ class TextEditorPresenter regions + setOverlayDimensions: (decorationId, itemWidth, itemHeight, contentMargin) -> + if overlayState = @state.content.overlays[decorationId] + dimensionsAreEqual = overlayState.itemWidth is itemWidth and + overlayState.itemHeight is itemHeight and + overlayState.contentMargin is contentMargin + unless dimensionsAreEqual + overlayState.itemWidth = itemWidth + overlayState.itemHeight = itemHeight + overlayState.contentMargin = contentMargin + @updateOverlaysState() + + getOverlayDimensions: (decorationId) -> + @state.content.overlays[decorationId] + observeCursor: (cursor) -> didChangePositionDisposable = cursor.onDidChangePosition => @updateHiddenInputState() if cursor.isLastCursor()