From 3aefa53b33eeea60c1496ae8054fc292655116e2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 2 Jun 2014 17:52:10 +0900 Subject: [PATCH] Batch together line and character width measurement after font changes This ensures we only perform a single update with the most up-to-date information about line height, default character width, and specific character widths. If this update causes more lines to be drawn we may measure again, but not necessarily. --- spec/editor-component-spec.coffee | 7 +++-- src/editor-component.coffee | 48 +++++++++++++++++-------------- src/lines-component.coffee | 11 +++---- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 061b4277a..c2f923c61 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -109,12 +109,12 @@ describe "EditorComponent", -> it "updates the top position of lines when the font family changes", -> # Can't find a font that changes the line height, but we think one might exist linesComponent = component.refs.lines - spyOn(linesComponent, 'measureLineHeightInPixelsAndCharWidth').andCallFake -> editor.setLineHeightInPixels(10) + spyOn(linesComponent, 'measureLineHeightAndDefaultCharWidth').andCallFake -> editor.setLineHeightInPixels(10) initialLineHeightInPixels = editor.getLineHeightInPixels() component.setFontFamily('sans-serif') - expect(linesComponent.measureLineHeightInPixelsAndCharWidth).toHaveBeenCalled() + expect(linesComponent.measureLineHeightAndDefaultCharWidth).toHaveBeenCalled() newLineHeightInPixels = editor.getLineHeightInPixels() expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels @@ -386,8 +386,9 @@ describe "EditorComponent", -> it "updates cursor positions when the line height changes", -> editor.setCursorBufferPosition([1, 10]) - cursorNode = node.querySelector('.cursor') component.setLineHeight(2) + cursorNode = node.querySelector('.cursor') + expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px, 0px)" expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px, 0px)" describe "selection rendering", -> diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 3a17ecbb3..1660a0f5d 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -35,7 +35,7 @@ EditorComponent = React.createClass scrollViewMeasurementRequested: false overflowChangedEventsPaused: false overflowChangedWhilePaused: false - measureLineHeightInPixelsAndCharWidthWhenShown: false + measureLineHeightAndDefaultCharWidthWhenShown: false render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state @@ -140,17 +140,21 @@ EditorComponent = React.createClass @observeConfig() componentDidMount: -> + {editor} = @props + @observeEditor() @listenForDOMEvents() @listenForCommands() - @measureScrollbars() + @subscribe atom.themes, 'stylesheet-added stylsheet-removed', @onStylesheetsChanged @subscribe scrollbarStyle.changes, @refreshScrollbars - @props.editor.setVisible(true) - @measureScrollView() + editor.setVisible(true) - @requestUpdate() + editor.batchUpdates => + @measureLineHeightAndDefaultCharWidth() + @measureScrollView() + @measureScrollbars() componentWillUnmount: -> @unsubscribe() @@ -163,11 +167,7 @@ EditorComponent = React.createClass @pendingChanges.length = 0 @refreshingScrollbars = false @measureScrollbars() if @measuringScrollbars - @measureLineHeightInPixelsAndCharWidthIfNeeded(prevState) - unless isEqualForProperties(prevState, @state, 'fontSize', 'fontFamily') - @refs.lines.clearScopedCharWidths() - @refs.lines.measureCharactersInNewLines() - + @measureLineHeightAndCharWidthsIfNeeded(prevState) @pauseOverflowChangedEvents() @props.parentView.trigger 'editor:display-updated' @@ -573,20 +573,24 @@ EditorComponent = React.createClass clientWidth = scrollViewNode.clientWidth editor.setWidth(clientWidth) if clientWidth > 0 - measureLineHeightInPixelsAndCharWidthIfNeeded: (prevState) -> - unless isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily') - if @state.visible - @measureLineHeightInPixelsAndCharWidth() - else - @measureLineHeightInPixelsAndCharWidthWhenShown = true - return + measureLineHeightAndCharWidthsIfNeeded: (prevState) -> + if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily') + @props.editor.batchUpdates => + if @state.visible + @measureLineHeightAndDefaultCharWidth() + else + @measureLineHeightAndDefaultCharWidthWhenShown = true - if @measureLineHeightInPixelsAndCharWidthWhenShown and @state.visible and not prevState.visible - @measureLineHeightInPixelsAndCharWidth() + unless isEqualForProperties(prevState, @state, 'fontSize', 'fontFamily') + @refs.lines.clearScopedCharWidths() + @refs.lines.measureCharactersInNewLines() - measureLineHeightInPixelsAndCharWidth: -> - @measureLineHeightInPixelsAndCharWidthWhenShown = false - @refs.lines.measureLineHeightInPixelsAndCharWidth() + else if @measureLineHeightAndDefaultCharWidthWhenShown and @state.visible and not prevState.visible + @measureLineHeightAndDefaultCharWidth() + + measureLineHeightAndDefaultCharWidth: -> + @measureLineHeightAndDefaultCharWidthWhenShown = false + @refs.lines.measureLineHeightAndDefaultCharWidth() measureScrollbars: -> @measuringScrollbars = false diff --git a/src/lines-component.coffee b/src/lines-component.coffee index b7b07cf62..9b8aa2cc5 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -30,9 +30,6 @@ LinesComponent = React.createClass @screenRowsByLineId = {} @lineIdsByScreenRow = {} - componentDidMount: -> - @measureLineHeightInPixelsAndCharWidth() - shouldComponentUpdate: (newProps) -> return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'selectionScreenRanges', 'lineHeightInPixels', 'defaultCharWidth', @@ -200,8 +197,7 @@ LinesComponent = React.createClass lineNodeForScreenRow: (screenRow) -> @lineNodesByLineId[@lineIdsByScreenRow[screenRow]] - measureLineHeightInPixelsAndCharWidth: -> - @measureWhenShown = false + measureLineHeightAndDefaultCharWidth: -> node = @getDOMNode() node.appendChild(DummyLineNode) lineHeightInPixels = DummyLineNode.getBoundingClientRect().height @@ -209,8 +205,9 @@ LinesComponent = React.createClass node.removeChild(DummyLineNode) {editor} = @props - editor.setLineHeightInPixels(lineHeightInPixels) - editor.setDefaultCharWidth(charWidth) + editor.batchUpdates -> + editor.setLineHeightInPixels(lineHeightInPixels) + editor.setDefaultCharWidth(charWidth) measureCharactersInNewLines: -> [visibleStartRow, visibleEndRow] = @props.renderedRowRange