Merge pull request #12448 from atom/as-editor-auto-width

Introduce autoWidth for TextEditors
This commit is contained in:
Nathan Sobo
2016-08-17 12:55:54 -06:00
committed by GitHub
6 changed files with 154 additions and 14 deletions

View File

@@ -4368,6 +4368,33 @@ describe('TextEditorComponent', function () {
})
})
describe('width', function () {
it('sizes the editor element according to the content width when auto width is true, or according to the container width otherwise', function () {
contentNode.style.width = '600px'
component.measureDimensions()
editor.setText("abcdefghi")
runAnimationFrames()
expect(wrapperNode.offsetWidth).toBe(contentNode.offsetWidth)
editor.update({autoWidth: true})
runAnimationFrames()
const editorWidth1 = wrapperNode.offsetWidth
expect(editorWidth1).toBeGreaterThan(0)
expect(editorWidth1).toBeLessThan(contentNode.offsetWidth)
editor.setText("abcdefghijkl")
editor.update({autoWidth: true})
runAnimationFrames()
const editorWidth2 = wrapperNode.offsetWidth
expect(editorWidth2).toBeGreaterThan(editorWidth1)
expect(editorWidth2).toBeLessThan(contentNode.offsetWidth)
editor.update({autoWidth: false})
runAnimationFrames()
expect(wrapperNode.offsetWidth).toBe(contentNode.offsetWidth)
})
})
describe('when the "mini" property is true', function () {
beforeEach(function () {
editor.setMini(true)

View File

@@ -425,6 +425,13 @@ describe "TextEditorPresenter", ->
editor.setMini(false)
expect(getState(presenter).horizontalScrollbar.visible).toBe true
it "is false when `editor.autoWidth` is true", ->
editor.update({autoWidth: true})
presenter = buildPresenter(explicitHeight: 10, contentFrameWidth: 30, verticalScrollbarWidth: 7, baseCharacterWidth: 10)
getState(presenter) # trigger a state update to store state in the presenter
editor.setText('abcdefghijklm')
expect(getState(presenter).horizontalScrollbar.visible).toBe(false)
describe ".height", ->
it "tracks the value of ::horizontalScrollbarHeight", ->
presenter = buildPresenter(horizontalScrollbarHeight: 10)
@@ -538,6 +545,21 @@ describe "TextEditorPresenter", ->
presenter.setScrollLeft(10)
expect(getState(presenter).content.scrollLeft).toBe 0
it "is always 0 when `editor.autoWidth` is true", ->
editor.update({autoWidth: true})
editor.setText('abcdefghijklm')
presenter = buildPresenter(explicitHeight: 10, contentFrameWidth: 30, verticalScrollbarWidth: 15, baseCharacterWidth: 10)
getState(presenter) # trigger a state update to store state in the presenter
editor.setCursorBufferPosition([0, Infinity])
editor.insertText('n')
expect(getState(presenter).content.scrollLeft).toBe(0)
editor.setText('abcdefghijklm\nnopqrstuvwxy') # make the vertical scrollbar appear
editor.setCursorBufferPosition([1, Infinity])
editor.insertText('z')
expect(getState(presenter).content.scrollLeft).toBe(0)
describe ".verticalScrollbar", ->
describe ".visible", ->
it "is true if the scrollHeight exceeds the computed client height", ->
@@ -755,6 +777,39 @@ describe "TextEditorPresenter", ->
expect(getState(presenter).hiddenInput.width).toBe 2
describe ".content", ->
describe '.width', ->
describe "when `editor.autoWidth` is false (the default)", ->
it "equals to the max width between the content frame width and the content width + the vertical scrollbar width", ->
editor.setText('abc\ndef\nghi\njkl')
presenter = buildPresenter(explicitHeight: 10, contentFrameWidth: 33, verticalScrollbarWidth: 7, baseCharacterWidth: 10)
expect(getState(presenter).content.width).toBe(3 * 10 + 7 + 1)
presenter.setContentFrameWidth(50)
expect(getState(presenter).content.width).toBe(50)
presenter.setVerticalScrollbarWidth(27)
expect(getState(presenter).content.width).toBe(3 * 10 + 27 + 1)
describe "when `editor.autoWidth` is true", ->
it "equals to the content width + the vertical scrollbar width", ->
editor.setText('abc\ndef\nghi\njkl')
presenter = buildPresenter(explicitHeight: 10, contentFrameWidth: 300, verticalScrollbarWidth: 7, baseCharacterWidth: 10)
expectStateUpdate presenter, -> editor.update({autoWidth: true})
expect(getState(presenter).content.width).toBe(3 * 10 + 7 + 1)
editor.setText('abcdefghi\n')
expect(getState(presenter).content.width).toBe(9 * 10 + 7 + 1)
it "ignores the vertical scrollbar width when it is unset", ->
editor.setText('abcdef\nghijkl')
presenter = buildPresenter(explicitHeight: 10, contentFrameWidth: 33, verticalScrollbarWidth: 7, baseCharacterWidth: 10)
presenter.setVerticalScrollbarWidth(null)
expect(getState(presenter).content.width).toBe(6 * 10 + 1)
it "ignores the content frame width when it is unset", ->
editor.setText('abc\ndef\nghi\njkl')
presenter = buildPresenter(explicitHeight: 10, contentFrameWidth: 33, verticalScrollbarWidth: 7, baseCharacterWidth: 10)
getState(presenter) # trigger a state update, causing verticalScrollbarWidth to be stored in the presenter
presenter.setContentFrameWidth(null)
expect(getState(presenter).content.width).toBe(3 * 10 + 7 + 1)
describe ".maxHeight", ->
it "changes based on boundingClientRect", ->
presenter = buildPresenter(scrollTop: 0, lineHeight: 10)
@@ -2664,6 +2719,17 @@ describe "TextEditorPresenter", ->
pixelPosition: {top: 10, left: 0}
}
describe ".width", ->
it "is null when `editor.autoWidth` is false (the default)", ->
presenter = buildPresenter(explicitHeight: 50, gutterWidth: 20, contentFrameWidth: 300, baseCharacterWidth: 10)
expect(getState(presenter).width).toBeNull()
it "equals to sum of .content.width and the width of the gutter when `editor.autoWidth` is true", ->
editor.setText('abcdef')
editor.update({autoWidth: true})
presenter = buildPresenter(explicitHeight: 50, gutterWidth: 20, contentFrameWidth: 300, baseCharacterWidth: 10)
expect(getState(presenter).width).toBe(20 + 6 * 10 + 1)
describe ".height", ->
it "updates model's rows per page when it changes", ->
presenter = buildPresenter(explicitHeight: 50, lineHeightInPixels: 10, horizontalScrollbarHeight: 10)

View File

@@ -5516,6 +5516,14 @@ describe "TextEditor", ->
editor.update({autoHeight: true})
expect(editor.getAutoHeight()).toBe(true)
describe "auto width", ->
it "returns false by default but can be customized", ->
expect(editor.getAutoWidth()).toBe(false)
editor.update({autoWidth: true})
expect(editor.getAutoWidth()).toBe(true)
editor.update({autoWidth: false})
expect(editor.getAutoWidth()).toBe(false)
describe '.get/setPlaceholderText()', ->
it 'can be created with placeholderText', ->
newEditor = atom.workspace.buildTextEditor(

View File

@@ -129,7 +129,7 @@ class TextEditorComponent
updateSync: ->
@updateSyncPreMeasurement()
@oldState ?= {}
@oldState ?= {width: null}
@newState = @presenter.getPostMeasurementState()
if @editor.getLastSelection()? and not @editor.getLastSelection().isEmpty()
@@ -149,6 +149,13 @@ class TextEditorComponent
else
@domNode.style.height = ''
if @newState.width isnt @oldState.width
if @newState.width?
@hostElement.style.width = @newState.width + 'px'
else
@hostElement.style.width = ''
@oldState.width = @newState.width
if @newState.gutters.length
@mountGutterContainerComponent() unless @gutterContainerComponent?
@gutterContainerComponent.updateSync(@newState)

View File

@@ -110,13 +110,14 @@ class TextEditorPresenter
@updateLines()
@updateFocusedState()
@updateHeightState()
@updateVerticalScrollState()
@updateHorizontalScrollState()
@updateScrollbarsState()
@updateHiddenInputState()
@updateContentState()
@updateFocusedState()
@updateHeightState()
@updateWidthState()
@updateHighlightDecorations() if @shouldUpdateDecorations
@updateTilesState()
@updateCursorsState()
@@ -224,6 +225,12 @@ class TextEditorPresenter
else
@state.height = null
updateWidthState: ->
if @model.getAutoWidth()
@state.width = @state.content.width + @gutterWidth
else
@state.width = null
updateVerticalScrollState: ->
@state.content.scrollHeight = @scrollHeight
@sharedGutterStyles.scrollHeight = @scrollHeight
@@ -269,7 +276,13 @@ class TextEditorPresenter
@sharedGutterStyles.maxHeight = @boundingClientRect.height
@state.content.maxHeight = @boundingClientRect.height
@state.content.width = Math.max(@contentWidth + @verticalScrollbarWidth, @contentFrameWidth)
verticalScrollbarWidth = @verticalScrollbarWidth ? 0
contentFrameWidth = @contentFrameWidth ? 0
contentWidth = @contentWidth ? 0
if @model.getAutoWidth()
@state.content.width = contentWidth + verticalScrollbarWidth
else
@state.content.width = Math.max(contentWidth + verticalScrollbarWidth, contentFrameWidth)
@state.content.scrollWidth = @scrollWidth
@state.content.scrollLeft = @scrollLeft
@state.content.backgroundColor = if @model.isMini() then null else @backgroundColor
@@ -662,6 +675,7 @@ class TextEditorPresenter
if @contentWidth isnt oldContentWidth
@updateScrollbarDimensions()
@updateClientWidth()
@updateScrollWidth()
updateClientHeight: ->
@@ -678,7 +692,11 @@ class TextEditorPresenter
updateClientWidth: ->
return unless @contentFrameWidth? and @verticalScrollbarWidth?
clientWidth = @contentFrameWidth - @verticalScrollbarWidth
if @model.getAutoWidth()
clientWidth = @contentWidth
else
clientWidth = @contentFrameWidth - @verticalScrollbarWidth
@model.setWidth(clientWidth, true) unless @editorWidthInChars
unless @clientWidth is clientWidth
@@ -720,20 +738,23 @@ class TextEditorPresenter
return unless @measuredVerticalScrollbarWidth? and @measuredHorizontalScrollbarHeight?
return unless @contentWidth? and @contentHeight?
clientWidthWithoutVerticalScrollbar = @contentFrameWidth
clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @measuredVerticalScrollbarWidth
clientHeightWithoutHorizontalScrollbar = @height
clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @measuredHorizontalScrollbarHeight
if @model.getAutoWidth()
clientWidthWithVerticalScrollbar = @contentWidth + @measuredVerticalScrollbarWidth
else
clientWidthWithVerticalScrollbar = @contentFrameWidth
clientWidthWithoutVerticalScrollbar = clientWidthWithVerticalScrollbar - @measuredVerticalScrollbarWidth
clientHeightWithHorizontalScrollbar = @height
clientHeightWithoutHorizontalScrollbar = clientHeightWithHorizontalScrollbar - @measuredHorizontalScrollbarHeight
horizontalScrollbarVisible =
not @model.isMini() and
(@contentWidth > clientWidthWithoutVerticalScrollbar or
@contentWidth > clientWidthWithVerticalScrollbar and @contentHeight > clientHeightWithoutHorizontalScrollbar)
(@contentWidth > clientWidthWithVerticalScrollbar or
@contentWidth > clientWidthWithoutVerticalScrollbar and @contentHeight > clientHeightWithHorizontalScrollbar)
verticalScrollbarVisible =
not @model.isMini() and
(@contentHeight > clientHeightWithoutHorizontalScrollbar or
@contentHeight > clientHeightWithHorizontalScrollbar and @contentWidth > clientWidthWithoutVerticalScrollbar)
(@contentHeight > clientHeightWithHorizontalScrollbar or
@contentHeight > clientHeightWithoutHorizontalScrollbar and @contentWidth > clientWidthWithVerticalScrollbar)
horizontalScrollbarHeight =
if horizontalScrollbarVisible
@@ -896,6 +917,9 @@ class TextEditorPresenter
@updateScrollHeight()
@updateEndRow()
didChangeAutoWidth: ->
@emitDidUpdateState()
setContentFrameWidth: (contentFrameWidth) ->
if @contentFrameWidth isnt contentFrameWidth or @editorWidthInChars?
oldContentFrameWidth = @contentFrameWidth

View File

@@ -127,7 +127,7 @@ class TextEditor extends Model
@softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength,
@softWrapped, @decorationManager, @selectionsMarkerLayer, @buffer, suppressCursorCreation,
@mini, @placeholderText, lineNumberGutterVisible, @largeFileMode, @clipboard,
@assert, grammar, @showInvisibles, @autoHeight, @scrollPastEnd, @editorWidthInChars,
@assert, grammar, @showInvisibles, @autoHeight, @autoWidth, @scrollPastEnd, @editorWidthInChars,
@tokenizedBuffer, @displayLayer, @invisibles, @showIndentGuide, @softWrapHangingIndentLength,
@softWrapped, @softWrapAtPreferredLineLength, @preferredLineLength
} = params
@@ -144,6 +144,7 @@ class TextEditor extends Model
@selections = []
@hasTerminatedPendingState = false
@autoWidth ?= false
@autoHeight ?= true
@mini ?= false
@scrollPastEnd ?= true
@@ -320,6 +321,10 @@ class TextEditor extends Model
@autoHeight = value
@editorElement?.didChangeAutoHeight()
when 'autoWidth'
if value isnt @autoWidth
@autoWidth = value
@presenter?.didChangeAutoWidth()
else
throw new TypeError("Invalid TextEditor parameter: '#{param}'")
@@ -3552,6 +3557,9 @@ class TextEditor extends Model
Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.")
@width
getAutoWidth: ->
@autoWidth
# Experimental: Scroll the editor such that the given screen row is at the
# top of the visible area.
setFirstVisibleScreenRow: (screenRow, fromView) ->