Enable subpixel font scaling

For certain font sizes, enabling `textRendering: optimizeLegibility` caused a
bunch of measurement-related issues. You can reproduce it by setting the following in your stylesheet:

```
atom-text-editor {
  font-size: 14px;
  text-rendering: optimizeLegibility;
}
```

Although I wanted to defer subpixel font scaling to a later moment, it seems
like Chrome needs to have it enabled in order to properly support the
"legibility" path for text rendering. (I guess this is part of the reason why
the Chromium team enabled it by default at some point in the past.)
This commit is contained in:
Antonio Scandurra 2015-10-02 15:47:27 +02:00
parent b7e373fdca
commit beb7896234
4 changed files with 99 additions and 81 deletions

View File

@ -70,10 +70,10 @@ describe "LinesYardstick", ->
expect(linesYardstick.pixelPositionForScreenPosition([0, 0])).toEqual({left: 0, top: 0})
expect(linesYardstick.pixelPositionForScreenPosition([0, 1])).toEqual({left: 7, top: 0})
expect(linesYardstick.pixelPositionForScreenPosition([0, 5])).toEqual({left: 38, top: 0})
expect(linesYardstick.pixelPositionForScreenPosition([1, 6])).toEqual({left: 42, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([1, 9])).toEqual({left: 72, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, Infinity])).toEqual({left: 280, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([0, 5])).toEqual({left: 37.8046875, top: 0})
expect(linesYardstick.pixelPositionForScreenPosition([1, 6])).toEqual({left: 43.20703125, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([1, 9])).toEqual({left: 72.20703125, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, Infinity])).toEqual({left: 288.046875, top: 28})
it "reuses already computed pixel positions unless it is invalidated", ->
atom.styles.addStyleSheet """
@ -83,9 +83,9 @@ describe "LinesYardstick", ->
}
"""
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 20, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 60, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 100, top: 70})
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 19.203125, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 57.609375, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 95.609375, top: 70})
atom.styles.addStyleSheet """
* {
@ -93,15 +93,15 @@ describe "LinesYardstick", ->
}
"""
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 20, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 60, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 100, top: 70})
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 19.203125, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 57.609375, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 95.609375, top: 70})
linesYardstick.invalidateCache()
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 24, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 72, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 120, top: 70})
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 24.00390625, top: 14})
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 72.01171875, top: 28})
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 120.01171875, top: 70})
it "correctly handles RTL characters", ->
atom.styles.addStyleSheet """
@ -114,8 +114,8 @@ describe "LinesYardstick", ->
editor.setText("السلام عليكم")
expect(linesYardstick.pixelPositionForScreenPosition([0, 0]).left).toBe 0
expect(linesYardstick.pixelPositionForScreenPosition([0, 1]).left).toBe 8
expect(linesYardstick.pixelPositionForScreenPosition([0, 2]).left).toBe 17
expect(linesYardstick.pixelPositionForScreenPosition([0, 5]).left).toBe 34
expect(linesYardstick.pixelPositionForScreenPosition([0, 2]).left).toBe 16
expect(linesYardstick.pixelPositionForScreenPosition([0, 5]).left).toBe 33
expect(linesYardstick.pixelPositionForScreenPosition([0, 7]).left).toBe 50
expect(linesYardstick.pixelPositionForScreenPosition([0, 9]).left).toBe 67
expect(linesYardstick.pixelPositionForScreenPosition([0, 11]).left).toBe 84
@ -133,13 +133,13 @@ describe "LinesYardstick", ->
"""
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 12.5})).toEqual([0, 2])
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 17.8})).toEqual([1, 3])
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 18.8})).toEqual([1, 3])
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100})).toEqual([2, 14])
expect(linesYardstick.screenPositionForPixelPosition({top: 32, left: 24.3})).toEqual([2, 3])
expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9])
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 99.9})).toEqual([5, 14])
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 221.5})).toEqual([5, 29])
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 222})).toEqual([5, 30])
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 224.4365234375})).toEqual([5, 29])
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 225})).toEqual([5, 30])
it "clips pixel positions above buffer start", ->
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]

View File

@ -958,8 +958,8 @@ describe "TextEditorComponent", ->
cursorNodes = componentNode.querySelectorAll('.cursor')
expect(cursorNodes.length).toBe 1
expect(cursorNodes[0].offsetHeight).toBe lineHeightInPixels
expect(cursorNodes[0].offsetWidth).toBe charWidth
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{5 * charWidth}px, #{0 * lineHeightInPixels}px)"
expect(cursorNodes[0].offsetWidth).toBeCloseTo charWidth, 0
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{Math.round(5 * charWidth)}px, #{0 * lineHeightInPixels}px)"
cursor2 = editor.addCursorAtScreenPosition([8, 11], autoscroll: false)
cursor3 = editor.addCursorAtScreenPosition([4, 10], autoscroll: false)
@ -968,8 +968,8 @@ describe "TextEditorComponent", ->
cursorNodes = componentNode.querySelectorAll('.cursor')
expect(cursorNodes.length).toBe 2
expect(cursorNodes[0].offsetTop).toBe 0
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{5 * charWidth}px, #{0 * lineHeightInPixels}px)"
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{10 * charWidth}px, #{4 * lineHeightInPixels}px)"
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{Math.round(5 * charWidth)}px, #{0 * lineHeightInPixels}px)"
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{Math.round(10 * charWidth)}px, #{4 * lineHeightInPixels}px)"
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
@ -980,13 +980,13 @@ describe "TextEditorComponent", ->
cursorNodes = componentNode.querySelectorAll('.cursor')
expect(cursorNodes.length).toBe 2
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{10 * charWidth - horizontalScrollbarNode.scrollLeft}px, #{4 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{11 * charWidth - horizontalScrollbarNode.scrollLeft}px, #{8 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{Math.round(10 * charWidth - horizontalScrollbarNode.scrollLeft)}px, #{4 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{Math.round(11 * charWidth - horizontalScrollbarNode.scrollLeft)}px, #{8 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
editor.onDidChangeCursorPosition cursorMovedListener = jasmine.createSpy('cursorMovedListener')
cursor3.setScreenPosition([4, 11], autoscroll: false)
nextAnimationFrame()
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{11 * charWidth - horizontalScrollbarNode.scrollLeft}px, #{4 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{Math.round(11 * charWidth - horizontalScrollbarNode.scrollLeft)}px, #{4 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
expect(cursorMovedListener).toHaveBeenCalled()
cursor3.destroy()
@ -994,7 +994,7 @@ describe "TextEditorComponent", ->
cursorNodes = componentNode.querySelectorAll('.cursor')
expect(cursorNodes.length).toBe 1
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{11 * charWidth - horizontalScrollbarNode.scrollLeft}px, #{8 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{Math.round(11 * charWidth - horizontalScrollbarNode.scrollLeft)}px, #{8 * lineHeightInPixels - verticalScrollbarNode.scrollTop}px)"
it "accounts for character widths when positioning cursors", ->
atom.config.set('editor.fontFamily', 'sans-serif')
@ -1010,8 +1010,8 @@ describe "TextEditorComponent", ->
range.setEnd(cursorLocationTextNode, 1)
rangeRect = range.getBoundingClientRect()
expect(cursorRect.left).toBe rangeRect.left
expect(cursorRect.width).toBe rangeRect.width
expect(cursorRect.left).toBeCloseTo rangeRect.left, 0
expect(cursorRect.width).toBeCloseTo rangeRect.width, 0
it "accounts for the width of paired characters when positioning cursors", ->
atom.config.set('editor.fontFamily', 'sans-serif')
@ -1029,8 +1029,8 @@ describe "TextEditorComponent", ->
range.setEnd(cursorLocationTextNode, 1)
rangeRect = range.getBoundingClientRect()
expect(cursorRect.left).toBe rangeRect.left
expect(cursorRect.width).toBe rangeRect.width
expect(cursorRect.left).toBeCloseTo rangeRect.left, 0
expect(cursorRect.width).toBeCloseTo rangeRect.width, 0
it "positions cursors correctly after character widths are changed via a stylesheet change", ->
atom.config.set('editor.fontFamily', 'sans-serif')
@ -1053,8 +1053,8 @@ describe "TextEditorComponent", ->
range.setEnd(cursorLocationTextNode, 1)
rangeRect = range.getBoundingClientRect()
expect(cursorRect.left).toBe rangeRect.left
expect(cursorRect.width).toBe rangeRect.width
expect(cursorRect.left).toBeCloseTo rangeRect.left, 0
expect(cursorRect.width).toBeCloseTo rangeRect.width, 0
atom.themes.removeStylesheet('test')
@ -1062,13 +1062,13 @@ describe "TextEditorComponent", ->
editor.setCursorScreenPosition([0, Infinity])
nextAnimationFrame()
cursorNode = componentNode.querySelector('.cursor')
expect(cursorNode.offsetWidth).toBe charWidth
expect(cursorNode.offsetWidth).toBeCloseTo charWidth, 0
it "gives the cursor a non-zero width even if it's inside atomic tokens", ->
editor.setCursorScreenPosition([1, 0])
nextAnimationFrame()
cursorNode = componentNode.querySelector('.cursor')
expect(cursorNode.offsetWidth).toBe charWidth
expect(cursorNode.offsetWidth).toBeCloseTo charWidth, 0
it "blinks cursors when they aren't moving", ->
cursorsNode = componentNode.querySelector('.cursors')
@ -1102,21 +1102,21 @@ describe "TextEditorComponent", ->
cursorNodes = componentNode.querySelectorAll('.cursor')
expect(cursorNodes.length).toBe 1
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{8 * charWidth}px, #{6 * lineHeightInPixels}px)"
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{Math.round(8 * charWidth)}px, #{6 * lineHeightInPixels}px)"
it "updates cursor positions when the line height changes", ->
editor.setCursorBufferPosition([1, 10])
component.setLineHeight(2)
nextAnimationFrame()
cursorNode = componentNode.querySelector('.cursor')
expect(cursorNode.style['-webkit-transform']).toBe "translate(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px)"
expect(cursorNode.style['-webkit-transform']).toBe "translate(#{Math.round(10 * editor.getDefaultCharWidth())}px, #{editor.getLineHeightInPixels()}px)"
it "updates cursor positions when the font size changes", ->
editor.setCursorBufferPosition([1, 10])
component.setFontSize(10)
nextAnimationFrame()
cursorNode = componentNode.querySelector('.cursor')
expect(cursorNode.style['-webkit-transform']).toBe "translate(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px)"
expect(cursorNode.style['-webkit-transform']).toBe "translate(#{Math.round(10 * editor.getDefaultCharWidth())}px, #{editor.getLineHeightInPixels()}px)"
it "updates cursor positions when the font family changes", ->
editor.setCursorBufferPosition([1, 10])
@ -1125,7 +1125,7 @@ describe "TextEditorComponent", ->
cursorNode = componentNode.querySelector('.cursor')
{left} = wrapperNode.pixelPositionForScreenPosition([1, 10])
expect(cursorNode.style['-webkit-transform']).toBe "translate(#{left}px, #{editor.getLineHeightInPixels()}px)"
expect(cursorNode.style['-webkit-transform']).toBe "translate(#{Math.round(left)}px, #{editor.getLineHeightInPixels()}px)"
describe "selection rendering", ->
[scrollViewNode, scrollViewClientLeft] = []
@ -1144,8 +1144,8 @@ describe "TextEditorComponent", ->
regionRect = regions[0].getBoundingClientRect()
expect(regionRect.top).toBe 1 * lineHeightInPixels
expect(regionRect.height).toBe 1 * lineHeightInPixels
expect(regionRect.left).toBe scrollViewClientLeft + 6 * charWidth
expect(regionRect.width).toBe 4 * charWidth
expect(regionRect.left).toBeCloseTo scrollViewClientLeft + 6 * charWidth, 0
expect(regionRect.width).toBeCloseTo 4 * charWidth, 0
it "renders 2 regions for 2-line selections", ->
editor.setSelectedScreenRange([[1, 6], [2, 10]])
@ -1157,14 +1157,14 @@ describe "TextEditorComponent", ->
region1Rect = regions[0].getBoundingClientRect()
expect(region1Rect.top).toBe 1 * lineHeightInPixels
expect(region1Rect.height).toBe 1 * lineHeightInPixels
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right
expect(region1Rect.left).toBeCloseTo scrollViewClientLeft + 6 * charWidth, 0
expect(region1Rect.right).toBeCloseTo tileNode.getBoundingClientRect().right, 0
region2Rect = regions[1].getBoundingClientRect()
expect(region2Rect.top).toBe 2 * lineHeightInPixels
expect(region2Rect.height).toBe 1 * lineHeightInPixels
expect(region2Rect.left).toBe scrollViewClientLeft + 0
expect(region2Rect.width).toBe 10 * charWidth
expect(region2Rect.left).toBeCloseTo scrollViewClientLeft + 0, 0
expect(region2Rect.width).toBeCloseTo 10 * charWidth, 0
it "renders 3 regions per tile for selections with more than 2 lines", ->
editor.setSelectedScreenRange([[0, 6], [5, 10]])
@ -1178,20 +1178,20 @@ describe "TextEditorComponent", ->
region1Rect = regions[0].getBoundingClientRect()
expect(region1Rect.top).toBe 0
expect(region1Rect.height).toBe 1 * lineHeightInPixels
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right
expect(region1Rect.left).toBeCloseTo scrollViewClientLeft + 6 * charWidth, 0
expect(region1Rect.right).toBeCloseTo tileNode.getBoundingClientRect().right, 0
region2Rect = regions[1].getBoundingClientRect()
expect(region2Rect.top).toBe 1 * lineHeightInPixels
expect(region2Rect.height).toBe 1 * lineHeightInPixels
expect(region2Rect.left).toBe scrollViewClientLeft + 0
expect(region2Rect.right).toBe tileNode.getBoundingClientRect().right
expect(region2Rect.left).toBeCloseTo scrollViewClientLeft + 0, 0
expect(region2Rect.right).toBeCloseTo tileNode.getBoundingClientRect().right, 0
region3Rect = regions[2].getBoundingClientRect()
expect(region3Rect.top).toBe 2 * lineHeightInPixels
expect(region3Rect.height).toBe 1 * lineHeightInPixels
expect(region3Rect.left).toBe scrollViewClientLeft + 0
expect(region3Rect.right).toBe tileNode.getBoundingClientRect().right
expect(region3Rect.left).toBeCloseTo scrollViewClientLeft + 0, 0
expect(region3Rect.right).toBeCloseTo tileNode.getBoundingClientRect().right, 0
# Tile 3
tileNode = component.tileNodesForLines()[1]
@ -1201,20 +1201,20 @@ describe "TextEditorComponent", ->
region1Rect = regions[0].getBoundingClientRect()
expect(region1Rect.top).toBe 3 * lineHeightInPixels
expect(region1Rect.height).toBe 1 * lineHeightInPixels
expect(region1Rect.left).toBe scrollViewClientLeft + 0
expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right
expect(region1Rect.left).toBeCloseTo scrollViewClientLeft + 0, 0
expect(region1Rect.right).toBeCloseTo tileNode.getBoundingClientRect().right, 0
region2Rect = regions[1].getBoundingClientRect()
expect(region2Rect.top).toBe 4 * lineHeightInPixels
expect(region2Rect.height).toBe 1 * lineHeightInPixels
expect(region2Rect.left).toBe scrollViewClientLeft + 0
expect(region2Rect.right).toBe tileNode.getBoundingClientRect().right
expect(region2Rect.left).toBeCloseTo scrollViewClientLeft + 0, 0
expect(region2Rect.right).toBeCloseTo tileNode.getBoundingClientRect().right, 0
region3Rect = regions[2].getBoundingClientRect()
expect(region3Rect.top).toBe 5 * lineHeightInPixels
expect(region3Rect.height).toBe 1 * lineHeightInPixels
expect(region3Rect.left).toBe scrollViewClientLeft + 0
expect(region3Rect.width).toBe 10 * charWidth
expect(region3Rect.left).toBeCloseTo scrollViewClientLeft + 0, 0
expect(region3Rect.width).toBeCloseTo 10 * charWidth, 0
it "does not render empty selections", ->
editor.addSelectionForBufferRange([[2, 2], [2, 2]])
@ -1237,7 +1237,7 @@ describe "TextEditorComponent", ->
nextAnimationFrame()
selectionNode = componentNode.querySelector('.region')
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
expect(selectionNode.offsetLeft).toBe 6 * editor.getDefaultCharWidth()
expect(selectionNode.offsetLeft).toBeCloseTo 6 * editor.getDefaultCharWidth(), 0
it "updates selections when the font family changes", ->
editor.setSelectedBufferRange([[1, 6], [1, 10]])
@ -1245,7 +1245,7 @@ describe "TextEditorComponent", ->
nextAnimationFrame()
selectionNode = componentNode.querySelector('.region')
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
expect(selectionNode.offsetLeft).toBe wrapperNode.pixelPositionForScreenPosition([1, 6]).left
expect(selectionNode.offsetLeft).toBeCloseTo wrapperNode.pixelPositionForScreenPosition([1, 6]).left, 0
it "will flash the selection when flash:true is passed to editor::setSelectedBufferRange", ->
editor.setSelectedBufferRange([[1, 6], [1, 10]], flash: true)
@ -1439,8 +1439,8 @@ describe "TextEditorComponent", ->
regionRect = regions[0].style
expect(regionRect.top).toBe (0 + 'px')
expect(regionRect.height).toBe 1 * lineHeightInPixels + 'px'
expect(regionRect.left).toBe 2 * charWidth + 'px'
expect(regionRect.width).toBe 2 * charWidth + 'px'
expect(regionRect.left).toBe Math.round(2 * charWidth) + 'px'
expect(regionRect.width).toBe Math.round(2 * charWidth) + 'px'
it "renders highlights decoration's marker is added", ->
regions = componentNode.querySelectorAll('.test-highlight .region')
@ -1606,7 +1606,7 @@ describe "TextEditorComponent", ->
position = wrapperNode.pixelPositionForBufferPosition([2, 10])
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
expect(overlay.style.left).toBe Math.round(position.left + gutterWidth) + 'px'
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
describe "positioning the overlay when near the edge of the editor", ->
@ -1614,10 +1614,10 @@ describe "TextEditorComponent", ->
beforeEach ->
atom.storeWindowDimensions()
itemWidth = 4 * editor.getDefaultCharWidth()
itemWidth = Math.round(4 * editor.getDefaultCharWidth())
itemHeight = 4 * editor.getLineHeightInPixels()
windowWidth = gutterWidth + 30 * editor.getDefaultCharWidth()
windowWidth = Math.round(gutterWidth + 30 * editor.getDefaultCharWidth())
windowHeight = 10 * editor.getLineHeightInPixels()
item.style.width = itemWidth + 'px'
@ -1645,7 +1645,7 @@ describe "TextEditorComponent", ->
position = wrapperNode.pixelPositionForBufferPosition([0, 26])
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
expect(overlay.style.left).toBe Math.round(position.left + gutterWidth) + 'px'
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
editor.insertText('a')
@ -1689,7 +1689,7 @@ describe "TextEditorComponent", ->
wrapperNode.focus() # updates via state change
nextAnimationFrame()
expect(inputNode.offsetTop).toBe (5 * lineHeightInPixels) - wrapperNode.getScrollTop()
expect(inputNode.offsetLeft).toBe (4 * charWidth) - wrapperNode.getScrollLeft()
expect(inputNode.offsetLeft).toBeCloseTo (4 * charWidth) - wrapperNode.getScrollLeft(), 0
# In bounds, not focused
inputNode.blur() # updates via state change
@ -2482,7 +2482,7 @@ describe "TextEditorComponent", ->
rightOfLongestLine = component.lineNodeForScreenRow(6).querySelector('.line > span:last-child').getBoundingClientRect().right
leftOfVerticalScrollbar = verticalScrollbarNode.getBoundingClientRect().left
expect(Math.round(rightOfLongestLine)).toBe leftOfVerticalScrollbar - 1 # Leave 1 px so the cursor is visible on the end of the line
expect(Math.round(rightOfLongestLine)).toBeCloseTo leftOfVerticalScrollbar - 1, 0 # Leave 1 px so the cursor is visible on the end of the line
it "only displays dummy scrollbars when scrollable in that direction", ->
expect(verticalScrollbarNode.style.display).toBe 'none'
@ -2963,7 +2963,7 @@ describe "TextEditorComponent", ->
cursorLeft = componentNode.querySelector('.cursor').getBoundingClientRect().left
line0Right = componentNode.querySelector('.line > span:last-child').getBoundingClientRect().right
expect(cursorLeft).toBe line0Right
expect(cursorLeft).toBeCloseTo line0Right, 0
describe "when the fontFamily changes while the editor is hidden", ->
it "does not attempt to measure the defaultCharWidth until the editor becomes visible again", ->
@ -2995,7 +2995,7 @@ describe "TextEditorComponent", ->
cursorLeft = componentNode.querySelector('.cursor').getBoundingClientRect().left
line0Right = componentNode.querySelector('.line > span:last-child').getBoundingClientRect().right
expect(cursorLeft).toBe line0Right
expect(cursorLeft).toBeCloseTo line0Right, 0
describe "when stylesheets change while the editor is hidden", ->
afterEach ->
@ -3021,7 +3021,7 @@ describe "TextEditorComponent", ->
cursorLeft = componentNode.querySelector('.cursor').getBoundingClientRect().left
line0Right = componentNode.querySelector('.line > span:last-child').getBoundingClientRect().right
expect(cursorLeft).toBe line0Right
expect(cursorLeft).toBeCloseTo line0Right, 0
describe "when lines are changed while the editor is hidden", ->
xit "does not measure new characters until the editor is shown again", ->
@ -3350,8 +3350,9 @@ describe "TextEditorComponent", ->
editor.setSelectedBufferRange([[5, 6], [6, 8]])
nextAnimationFrame()
right = wrapperNode.pixelPositionForBufferPosition([6, 8 + editor.getHorizontalScrollMargin()]).left
expect(wrapperNode.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
expect(wrapperNode.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10
expect(wrapperNode.getScrollRight()).toBeCloseTo right, 0
editor.setSelectedBufferRange([[0, 0], [0, 0]])
nextAnimationFrame()
@ -3361,14 +3362,16 @@ describe "TextEditorComponent", ->
editor.setSelectedBufferRange([[6, 6], [6, 8]])
nextAnimationFrame()
expect(wrapperNode.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
expect(wrapperNode.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10
expect(wrapperNode.getScrollRight()).toBeCloseTo right, 0
describe "when adding selections for buffer ranges", ->
it "autoscrolls to the added selection if needed", ->
editor.addSelectionForBufferRange([[8, 10], [8, 15]])
nextAnimationFrame()
right = wrapperNode.pixelPositionForBufferPosition([8, 15]).left
expect(wrapperNode.getScrollBottom()).toBe (9 * 10) + (2 * 10)
expect(wrapperNode.getScrollRight()).toBe (15 * 10) + (2 * 10)
expect(wrapperNode.getScrollRight()).toBeCloseTo(right + 2 * 10, 0)
describe "when selecting lines containing cursors", ->
it "autoscrolls to the selection", ->
@ -3406,9 +3409,10 @@ describe "TextEditorComponent", ->
editor.scrollToCursorPosition()
nextAnimationFrame()
right = wrapperNode.pixelPositionForScreenPosition([8, 9 + editor.getHorizontalScrollMargin()]).left
expect(wrapperNode.getScrollTop()).toBe (8.8 * 10) - 30
expect(wrapperNode.getScrollBottom()).toBe (8.3 * 10) + 30
expect(wrapperNode.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10
expect(wrapperNode.getScrollRight()).toBeCloseTo right, 0
wrapperNode.setScrollTop(0)
editor.scrollToCursorPosition(center: false)
@ -3460,11 +3464,13 @@ describe "TextEditorComponent", ->
editor.moveRight()
nextAnimationFrame()
expect(wrapperNode.getScrollRight()).toBe 6 * 10
right = wrapperNode.pixelPositionForScreenPosition([0, 6]).left
expect(wrapperNode.getScrollRight()).toBeCloseTo right, 0
editor.moveRight()
nextAnimationFrame()
expect(wrapperNode.getScrollRight()).toBe 7 * 10
right = wrapperNode.pixelPositionForScreenPosition([0, 7]).left
expect(wrapperNode.getScrollRight()).toBeCloseTo right, 0
it "scrolls left when the last cursor gets closer than ::horizontalScrollMargin to the left of the editor", ->
wrapperNode.setScrollRight(wrapperNode.getScrollWidth())
@ -3475,11 +3481,13 @@ describe "TextEditorComponent", ->
editor.moveLeft()
nextAnimationFrame()
expect(wrapperNode.getScrollLeft()).toBe 59 * 10
left = wrapperNode.pixelPositionForScreenPosition([6, 59]).left
expect(wrapperNode.getScrollLeft()).toBeCloseTo left, 0
editor.moveLeft()
nextAnimationFrame()
expect(wrapperNode.getScrollLeft()).toBe 58 * 10
left = wrapperNode.pixelPositionForScreenPosition([6, 58]).left
expect(wrapperNode.getScrollLeft()).toBeCloseTo left, 0
it "scrolls down when inserting lines makes the document longer than the editor's height", ->
editor.setCursorScreenPosition([13, Infinity])

View File

@ -28,7 +28,7 @@ class AtomWindow
title: 'Atom'
'web-preferences':
'direct-write': true
'subpixel-font-scaling': false
'subpixel-font-scaling': true
# Don't set icon on Windows so the exe's ico will be used as window and
# taskbar's icon. See https://github.com/atom/atom/issues/4811 for more.
if process.platform is 'linux'

View File

@ -501,7 +501,7 @@ class TextEditorPresenter
return unless cursor.isVisible() and @startRow <= screenRange.start.row < @endRow
pixelRect = @pixelRectForScreenRange(screenRange)
pixelRect.width = @baseCharacterWidth if pixelRect.width is 0
pixelRect.width = Math.round(@baseCharacterWidth) if pixelRect.width is 0
@state.content.cursors[cursor.id] = pixelRect
updateOverlaysState: ->
@ -1134,6 +1134,10 @@ class TextEditorPresenter
@linesYardstick.pixelPositionForScreenPosition(screenPosition, clip)
position.top -= @getScrollTop()
position.left -= @getScrollLeft()
position.top = Math.round(position.top)
position.left = Math.round(position.left)
position
hasPixelRectRequirements: ->
@ -1146,6 +1150,12 @@ class TextEditorPresenter
rect = @linesYardstick.pixelRectForScreenRange(screenRange)
rect.top -= @getScrollTop()
rect.left -= @getScrollLeft()
rect.top = Math.round(rect.top)
rect.left = Math.round(rect.left)
rect.width = Math.round(rect.width)
rect.height = Math.round(rect.height)
rect
observeDecoration: (decoration) ->
@ -1507,10 +1517,10 @@ class TextEditorPresenter
@emitDidUpdateState()
getVerticalScrollMarginInPixels: ->
@model.getVerticalScrollMargin() * @lineHeight
Math.round(@model.getVerticalScrollMargin() * @lineHeight)
getHorizontalScrollMarginInPixels: ->
@model.getHorizontalScrollMargin() * @baseCharacterWidth
Math.round(@model.getHorizontalScrollMargin() * @baseCharacterWidth)
getVerticalScrollbarWidth: ->
@verticalScrollbarWidth