Comparar commits

...

23 Commits

Autor SHA1 Mensagem Data
Nathan Sobo fc24d687dc Move the lines individually 2014-05-09 14:37:49 -06:00
Nathan Sobo f123a9f149 Use line number and soft wrap count as key for line numbers in gutter 2014-05-07 15:36:18 -06:00
Nathan Sobo 7159b095b1 Get specs passing; use px unit on all transform dimensions always 2014-05-07 15:31:00 -06:00
Nathan Sobo 822429812a Render each line as a texture on the GPU 2014-05-07 14:58:32 -06:00
Nathan Sobo 12b8055a46 Base the visible lines on the height instead of the clientHeight
Because overlay scrollbars are invisible unless scrolling and even
regular scrollbars can be transparent.
2014-05-07 14:39:42 -06:00
Nathan Sobo d8a1a84ccf Refresh scrollbars when overlay/classic style preferences changes 2014-05-07 14:38:14 -06:00
Nathan Sobo c8dccdfe9c Only refresh editor scrollbars on stylesheet changes that require it
If a stylesheet is added or removed, we check if it contains a selector
for scrollbars before refreshing anything.
2014-05-07 14:30:16 -06:00
Nathan Sobo 3ecc070a9d Emit stylesheet-added/removed from ThemeManager w/ CSSStyleSheet objects
This enables subscribers to detect not just that stylesheets have
changed, but specifically how they have changed. This is used by the
React editor component to only refresh scrollbars when a stylesheet
that actually contains selectors for scrollbar elements is added or
removed.
2014-05-07 14:30:16 -06:00
Nathan Sobo b4acb71d8e Default scrollbar height/width to 15 when it's measure as 0
A 0 measurement indicates that overlay scrollbars are enabled, so we
just fall back to 15px in that case so the user can hover directly over
the scrollbar to scroll.
2014-05-07 14:30:16 -06:00
Nathan Sobo 29a956a29d Hide and show scrollbars before measuring them after stylesheet changes
We measure the scrollbar-corner node when there's a stylesheet change,
but Chromium won't apply the new style if it was already visible before
the change. This commit hides and shows it before measuring so we get
accurate values.
2014-05-07 14:30:16 -06:00
Nathan Sobo 95feac4789 Explicitly assign dummy scrollbars to the correct width/height
Previously, dummy scrollbars were always 15px wide/tall. This caused
them to obscure the ability to click for the entire 15px region, even if
the actual scrollbar was styled to be much thinner. Now we explicitly
measure the size of scrollbars on mount and when the stylesheets change
and set the height/width explicitly.
2014-05-07 14:30:16 -06:00
Nathan Sobo dc6c6b2a53 Hide scrollbar when not scrollable in a given direction 2014-05-07 14:30:16 -06:00
Nathan Sobo 9defc0b70a Add a dummy scrollbar corner
Horizontal / vertical scrollbars render a 'corner' on the lower right
when they would otherwise overlap. I previously relied on drawing both
dummy scrollbars at their full width/height so the corner got rendered,
but that interfered with the display of the horizontal scrollbar in
certain circumstances because it was too wide to scroll. This commit
provides that behavior with an absolutely positioned div with the same
dimensions as the intersection of scrollbars when both are visible.
2014-05-07 14:30:16 -06:00
Nathan Sobo 9b7260d94d Don't allow spec window size to interfere with the size of the editor 2014-05-07 14:30:16 -06:00
Nathan Sobo 7e208bb413 Don't obscure last character of long lines with vertical scrollbar
This entailed quite a few changes to dial in scrollbars. The scrollbars
are now adjusted in size to account for the width of the opposite
scrollbar. If the width or height are not explicitly constrained and we
are scrollable in the opposite direction that is constrained, we account
for the width of the opposite scrollbar in assigning a natural height
or width based on the content.
2014-05-07 14:30:16 -06:00
Nathan Sobo 92737e2e9f Account for gutter width in scrollWidth of horizontal scrollbar
Because the scrollbar now spans the entire editor but the scrollable
area does not include the gutter, we need to add the current width of
the gutter to the scroll width of the horizontal scrollbar to allow
it to scroll to the end of the longest lines.
2014-05-07 14:30:16 -06:00
Nathan Sobo 2706490518 Remove stray return in spec 2014-05-07 14:30:16 -06:00
Nathan Sobo 4b9e001ca1 Update line number padding when max digits changes 2014-05-07 14:30:15 -06:00
Nathan Sobo ebeb0cc652 Remove logging 2014-05-07 14:30:15 -06:00
Nathan Sobo 333f88e2c3 Fix specs that broke when accounting for horizontal scrollbar height 2014-05-07 14:30:15 -06:00
Nathan Sobo aa2f5b09f9 Respect horizontal scrollbar when rendering the vertical, and vice versa
We set overflow to hidden in the opposite scroll direction only if we
can't actually scroll in that direction, causing the white square where
neither scrollbar overlaps to appear at the lower right corner.
2014-05-07 14:30:15 -06:00
Nathan Sobo d27c1d1e76 Update scrollTop to valid position when scrollbar disappears 2014-05-07 14:30:15 -06:00
Nathan Sobo 9e301aa988 Revert "Revert "Don't obscure the last line of the editor with the horizontal scrollbar""
This reverts commit 1d634e471e.
2014-05-07 14:30:15 -06:00
14 arquivos alterados com 439 adições e 76 exclusões
+2
Ver Arquivo
@@ -979,6 +979,7 @@ describe "DisplayBuffer", ->
describe "::setScrollLeft", ->
beforeEach ->
displayBuffer.manageScrollPosition = true
displayBuffer.setLineHeight(10)
displayBuffer.setDefaultCharWidth(10)
it "disallows negative values", ->
@@ -1001,6 +1002,7 @@ describe "DisplayBuffer", ->
displayBuffer.manageScrollPosition = true
displayBuffer.setLineHeight(10)
displayBuffer.setDefaultCharWidth(10)
displayBuffer.setHorizontalScrollbarHeight(0)
displayBuffer.setHeight(50)
displayBuffer.setWidth(50)
+141 -16
Ver Arquivo
@@ -1,9 +1,9 @@
{extend, flatten, toArray} = require 'underscore-plus'
{extend, flatten, toArray, last} = require 'underscore-plus'
ReactEditorView = require '../src/react-editor-view'
nbsp = String.fromCharCode(160)
describe "EditorComponent", ->
[editor, wrapperView, component, node, verticalScrollbarNode, horizontalScrollbarNode] = []
[contentNode, editor, wrapperView, component, node, verticalScrollbarNode, horizontalScrollbarNode] = []
[lineHeightInPixels, charWidth, delayAnimationFrames, nextAnimationFrame] = []
beforeEach ->
@@ -26,6 +26,9 @@ describe "EditorComponent", ->
atom.project.open('sample.js').then (o) -> editor = o
runs ->
contentNode = document.querySelector('#jasmine-content')
contentNode.style.width = '1000px'
wrapperView = new ReactEditorView(editor)
wrapperView.attachToDom()
{component} = wrapperView
@@ -38,6 +41,9 @@ describe "EditorComponent", ->
verticalScrollbarNode = node.querySelector('.vertical-scrollbar')
horizontalScrollbarNode = node.querySelector('.horizontal-scrollbar')
afterEach ->
contentNode.style.width = ''
describe "line rendering", ->
it "renders only the currently-visible lines", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
@@ -51,28 +57,29 @@ describe "EditorComponent", ->
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
expect(node.querySelector('.scroll-view-content').style['-webkit-transform']).toBe "translate3d(0px, #{-2.5 * lineHeightInPixels}px, 0)"
expect(node.querySelector('.scroll-view-content').style['-webkit-transform']).toBe "translate3d(0px, #{-2.5 * lineHeightInPixels}px, 0px)"
lineNodes = node.querySelectorAll('.line')
expect(lineNodes.length).toBe 6
expect(lineNodes[0].offsetTop).toBe 2 * lineHeightInPixels
expect(lineNodes[0].style['-webkit-transform']).toBe "translate3d(0px, #{2 * lineHeightInPixels}px, 0px)"
expect(lineNodes[0].textContent).toBe editor.lineForScreenRow(2).text
expect(lineNodes[5].textContent).toBe editor.lineForScreenRow(7).text
it "updates absolute positions of subsequent lines when lines are inserted or removed", ->
it "updates transforms of subsequent lines when lines are inserted or removed", ->
editor.getBuffer().deleteRows(0, 1)
lineNodes = node.querySelectorAll('.line')
expect(lineNodes[0].offsetTop).toBe 0
expect(lineNodes[1].offsetTop).toBe 1 * lineHeightInPixels
expect(lineNodes[2].offsetTop).toBe 2 * lineHeightInPixels
expect(lineNodes[0].style['-webkit-transform']).toBe "translate3d(0px, #{0 * lineHeightInPixels}px, 0px)"
expect(lineNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * lineHeightInPixels}px, 0px)"
expect(lineNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * lineHeightInPixels}px, 0px)"
editor.getBuffer().insert([0, 0], '\n\n')
lineNodes = node.querySelectorAll('.line')
expect(lineNodes[0].offsetTop).toBe 0
expect(lineNodes[1].offsetTop).toBe 1 * lineHeightInPixels
expect(lineNodes[2].offsetTop).toBe 2 * lineHeightInPixels
expect(lineNodes[3].offsetTop).toBe 3 * lineHeightInPixels
expect(lineNodes[4].offsetTop).toBe 4 * lineHeightInPixels
expect(lineNodes[0].style['-webkit-transform']).toBe "translate3d(0px, #{0 * lineHeightInPixels}px, 0px)"
expect(lineNodes[1].style['-webkit-transform']).toBe "translate3d(0px, #{1 * lineHeightInPixels}px, 0px)"
expect(lineNodes[2].style['-webkit-transform']).toBe "translate3d(0px, #{2 * lineHeightInPixels}px, 0px)"
expect(lineNodes[3].style['-webkit-transform']).toBe "translate3d(0px, #{3 * lineHeightInPixels}px, 0px)"
expect(lineNodes[4].style['-webkit-transform']).toBe "translate3d(0px, #{4 * lineHeightInPixels}px, 0px)"
describe "when indent guides are enabled", ->
beforeEach ->
@@ -146,7 +153,7 @@ describe "EditorComponent", ->
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
expect(node.querySelector('.line-numbers').style['-webkit-transform']).toBe "translate3d(0, #{-2.5 * lineHeightInPixels}px, 0)"
expect(node.querySelector('.line-numbers').style['-webkit-transform']).toBe "translate3d(0px, #{-2.5 * lineHeightInPixels}px, 0px)"
lineNumberNodes = node.querySelectorAll('.line-number')
expect(lineNumberNodes.length).toBe 6
@@ -188,6 +195,20 @@ describe "EditorComponent", ->
expect(lines[4].textContent).toBe "#{nbsp}3"
expect(lines[5].textContent).toBe "#{nbsp}"
it "pads line numbers to be right justified based on the maximum number of line number digits", ->
editor.getBuffer().setText([1..10].join('\n'))
lineNumberNodes = toArray(node.querySelectorAll('.line-number'))
for node, i in lineNumberNodes[0..8]
expect(node.textContent).toBe "#{nbsp}#{i + 1}"
expect(lineNumberNodes[9].textContent).toBe '10'
# Removes padding when the max number of digits goes down
editor.getBuffer().delete([[1, 0], [2, 0]])
lineNumberNodes = toArray(node.querySelectorAll('.line-number'))
for node, i in lineNumberNodes
expect(node.textContent).toBe "#{i + 1}"
describe "cursor rendering", ->
it "renders the currently visible cursors", ->
cursor1 = editor.getCursor()
@@ -512,11 +533,11 @@ describe "EditorComponent", ->
component.measureHeightAndWidth()
scrollViewContentNode = node.querySelector('.scroll-view-content')
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0)"
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
expect(horizontalScrollbarNode.scrollLeft).toBe 0
editor.setScrollLeft(100)
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(-100px, 0px, 0)"
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(-100px, 0px, 0px)"
expect(horizontalScrollbarNode.scrollLeft).toBe 100
it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", ->
@@ -529,6 +550,110 @@ describe "EditorComponent", ->
expect(editor.getScrollLeft()).toBe 100
it "does not obscure the last line with the horizontal scrollbar", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
editor.setScrollBottom(editor.getScrollHeight())
lastLineNode = last(node.querySelectorAll('.line'))
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
topOfHorizontalScrollbar = horizontalScrollbarNode.getBoundingClientRect().top
expect(bottomOfLastLine).toBe topOfHorizontalScrollbar
# Scroll so there's no space below the last line when the horizontal scrollbar disappears
node.style.width = 100 * charWidth + 'px'
component.measureHeightAndWidth()
lastLineNode = last(node.querySelectorAll('.line'))
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
bottomOfEditor = node.getBoundingClientRect().bottom
expect(bottomOfLastLine).toBe bottomOfEditor
it "does not obscure the last character of the longest line with the vertical scrollbar", ->
node.style.height = 7 * lineHeightInPixels + 'px'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
editor.setScrollLeft(Infinity)
lineNodes = node.querySelectorAll('.line')
rightOfLongestLine = lineNodes[6].getBoundingClientRect().right
leftOfVerticalScrollbar = verticalScrollbarNode.getBoundingClientRect().left
expect(rightOfLongestLine).toBe leftOfVerticalScrollbar - 1 # 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'
expect(horizontalScrollbarNode.style.display).toBe 'none'
node.style.height = 4.5 * lineHeightInPixels + 'px'
node.style.width = '1000px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.display).toBe ''
expect(horizontalScrollbarNode.style.display).toBe 'none'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.display).toBe ''
expect(horizontalScrollbarNode.style.display).toBe ''
node.style.height = 20 * lineHeightInPixels + 'px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.display).toBe 'none'
expect(horizontalScrollbarNode.style.display).toBe ''
it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", ->
node.style.height = 4 * lineHeightInPixels + 'px'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
atom.themes.applyStylesheet "test", """
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
"""
scrollbarCornerNode = node.querySelector('.scrollbar-corner')
expect(verticalScrollbarNode.offsetWidth).toBe 8
expect(horizontalScrollbarNode.offsetHeight).toBe 8
expect(scrollbarCornerNode.offsetWidth).toBe 8
expect(scrollbarCornerNode.offsetHeight).toBe 8
it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", ->
scrollbarCornerNode = node.querySelector('.scrollbar-corner')
expect(verticalScrollbarNode.style.bottom).toBe ''
expect(horizontalScrollbarNode.style.right).toBe ''
node.style.height = 4.5 * lineHeightInPixels + 'px'
node.style.width = '1000px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.bottom).toBe ''
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
expect(scrollbarCornerNode.style.display).toBe 'none'
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
expect(scrollbarCornerNode.style.display).toBe ''
node.style.height = 20 * lineHeightInPixels + 'px'
component.measureHeightAndWidth()
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
expect(horizontalScrollbarNode.style.right).toBe ''
expect(scrollbarCornerNode.style.display).toBe 'none'
it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
gutterNode = node.querySelector('.gutter')
node.style.width = 10 * charWidth + 'px'
component.measureHeightAndWidth()
expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth()
describe "when a mousewheel event occurs on the editor", ->
it "updates the horizontal or vertical scrollbar depending on which delta is greater (x or y)", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
+3
Ver Arquivo
@@ -698,6 +698,7 @@ describe "Editor", ->
editor.setHorizontalScrollMargin(2)
editor.setLineHeight(10)
editor.setDefaultCharWidth(10)
editor.setHorizontalScrollbarHeight(0)
editor.setHeight(5.5 * 10)
editor.setWidth(5.5 * 10)
@@ -1138,6 +1139,7 @@ describe "Editor", ->
editor.setDefaultCharWidth(10)
editor.setHeight(50)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
expect(editor.getScrollTop()).toBe 0
editor.setSelectedBufferRange([[5, 6], [6, 8]], autoscroll: true)
@@ -3094,6 +3096,7 @@ describe "Editor", ->
editor.setDefaultCharWidth(10)
editor.setHeight(50)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
expect(editor.getScrollTop()).toBe 0
expect(editor.getScrollLeft()).toBe 0
+32 -5
Ver Arquivo
@@ -131,15 +131,21 @@ describe "ThemeManager", ->
describe "requireStylesheet(path)", ->
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
cssPath = atom.project.resolve('css.css')
lengthBefore = $('head style').length
themeManager.requireStylesheet(cssPath)
expect($('head style').length).toBe lengthBefore + 1
expect(stylesheetAddedHandler).toHaveBeenCalled()
expect(stylesheetsChangedHandler).toHaveBeenCalled()
element = $('head style[id*="css.css"]')
expect(element.attr('id')).toBe themeManager.stringToId(cssPath)
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
# doesn't append twice
themeManager.requireStylesheet(cssPath)
@@ -187,9 +193,18 @@ describe "ThemeManager", ->
themeManager.requireStylesheet(cssPath)
expect($(document.body).css('font-weight')).toBe("bold")
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
themeManager.removeStylesheet(cssPath)
expect($(document.body).css('font-weight')).not.toBe("bold")
expect(stylesheetRemovedHandler).toHaveBeenCalled()
stylesheet = stylesheetRemovedHandler.argsForCall[0][0]
expect(stylesheet instanceof CSSStyleSheet).toBe true
expect(stylesheet.cssRules[0].selectorText).toBe 'body'
expect(stylesheetsChangedHandler).toHaveBeenCalled()
describe "base stylesheet loading", ->
@@ -219,20 +234,21 @@ describe "ThemeManager", ->
describe "when the user stylesheet changes", ->
it "reloads it", ->
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less')
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
spyOn(themeManager, 'getUserStylesheetPath').andReturn userStylesheetPath
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
waitsForPromise ->
themeManager.activateThemes()
runs ->
expect($(document.body).css('border-style')).toBe 'dotted'
expect(stylesheetsChangedHandler).toHaveBeenCalled()
stylesheetsChangedHandler.reset()
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
expect($(document.body).css('border-style')).toBe 'dotted'
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
waitsFor ->
@@ -240,7 +256,16 @@ describe "ThemeManager", ->
runs ->
expect($(document.body).css('border-style')).toBe 'dashed'
expect(stylesheetRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted'
expect(stylesheetAddedHandler).toHaveBeenCalled()
expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
expect(stylesheetsChangedHandler).toHaveBeenCalled()
stylesheetRemovedHandler.reset()
stylesheetsChangedHandler.reset()
fs.removeSync(userStylesheetPath)
@@ -248,6 +273,8 @@ describe "ThemeManager", ->
themeManager.loadUserStylesheet.callCount is 2
runs ->
expect(stylesheetRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
expect($(document.body).css('border-style')).toBe 'none'
expect(stylesheetsChangedHandler).toHaveBeenCalled()
+61 -6
Ver Arquivo
@@ -32,6 +32,8 @@ class DisplayBuffer extends Model
verticalScrollMargin: 2
horizontalScrollMargin: 6
horizontalScrollbarHeight: 15
verticalScrollbarWidth: 15
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
super
@@ -111,32 +113,83 @@ class DisplayBuffer extends Model
getHorizontalScrollMargin: -> @horizontalScrollMargin
setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin
getHeight: -> @height ? @getScrollHeight()
getHorizontalScrollbarHeight: -> @horizontalScrollbarHeight
setHorizontalScrollbarHeight: (@horizontalScrollbarHeight) -> @horizontalScrollbarHeight
getVerticalScrollbarWidth: -> @verticalScrollbarWidth
setVerticalScrollbarWidth: (@verticalScrollbarWidth) -> @verticalScrollbarWidth
getHeight: ->
if @height?
@height
else
if @horizontallyScrollable()
@getScrollHeight() + @getHorizontalScrollbarHeight()
else
@getScrollHeight()
setHeight: (@height) -> @height
getWidth: -> @width ? @getScrollWidth()
getClientHeight: (reentrant) ->
if @horizontallyScrollable(reentrant)
@getHeight() - @getHorizontalScrollbarHeight()
else
@getHeight()
getClientWidth: (reentrant) ->
if @verticallyScrollable(reentrant)
@getWidth() - @getVerticalScrollbarWidth()
else
@getWidth()
horizontallyScrollable: (reentrant) ->
return false unless @width?
return false if @getSoftWrap()
if reentrant
@getScrollWidth() > @getWidth()
else
@getScrollWidth() > @getClientWidth(true)
verticallyScrollable: (reentrant) ->
return false unless @height?
if reentrant
@getScrollHeight() > @getHeight()
else
@getScrollHeight() > @getClientHeight(true)
getWidth: ->
if @width?
@width
else
if @verticallyScrollable()
@getScrollWidth() + @getVerticalScrollbarWidth()
else
@getScrollWidth()
setWidth: (newWidth) ->
oldWidth = @width
@width = newWidth
@updateWrappedScreenLines() if newWidth isnt oldWidth and @softWrap
@setScrollTop(@getScrollTop()) # Ensure scrollTop is still valid in case horizontal scrollbar disappeared
@width
getScrollTop: -> @scrollTop
setScrollTop: (scrollTop) ->
if @manageScrollPosition
@scrollTop = Math.max(0, Math.min(@getScrollHeight() - @getHeight(), scrollTop))
@scrollTop = Math.max(0, Math.min(@getScrollHeight() - @getClientHeight(), scrollTop))
else
@scrollTop = scrollTop
getScrollBottom: -> @scrollTop + @height
setScrollBottom: (scrollBottom) ->
@setScrollTop(scrollBottom - @height)
@setScrollTop(scrollBottom - @getClientHeight())
@getScrollBottom()
getScrollLeft: -> @scrollLeft
setScrollLeft: (scrollLeft) ->
if @manageScrollPosition
@scrollLeft = Math.max(0, Math.min(@getScrollWidth() - @getWidth(), scrollLeft))
@scrollLeft = Math.max(0, Math.min(@getScrollWidth() - @getClientWidth(), scrollLeft))
@scrollLeft
else
@scrollLeft = scrollLeft
@@ -151,6 +204,8 @@ class DisplayBuffer extends Model
getDefaultCharWidth: -> @defaultCharWidth
setDefaultCharWidth: (@defaultCharWidth) -> @defaultCharWidth
getCursorWidth: -> 1
getScopedCharWidth: (scopeNames, char) ->
@getScopedCharWidths(scopeNames)[char]
@@ -178,7 +233,7 @@ class DisplayBuffer extends Model
@getLineCount() * @getLineHeight()
getScrollWidth: ->
@getMaxLineLength() * @getDefaultCharWidth()
(@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth()
getVisibleRowRange: ->
unless @getLineHeight() > 0
+81 -6
Ver Arquivo
@@ -1,10 +1,12 @@
React = require 'react'
{div, span} = require 'reactionary'
{debounce} = require 'underscore-plus'
scrollbarStyle = require 'scrollbar-style'
GutterComponent = require './gutter-component'
EditorScrollViewComponent = require './editor-scroll-view-component'
ScrollbarComponent = require './scrollbar-component'
ScrollbarCornerComponent = require './scrollbar-corner-component'
SubscriberMixin = require './subscriber-mixin'
module.exports =
@@ -20,10 +22,15 @@ EditorComponent = React.createClass
cursorsMoved: false
preservedRowRange: null
scrollingVertically: false
gutterWidth: 0
refreshingScrollbars: false
measuringScrollbars: true
render: ->
{focused, fontSize, lineHeight, fontFamily, showIndentGuide} = @state
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
maxLineNumberDigits = editor.getScreenLineCount().toString().length
if @isMounted()
renderedRowRange = @getRenderedRowRange()
scrollHeight = editor.getScrollHeight()
@@ -31,14 +38,19 @@ EditorComponent = React.createClass
scrollTop = editor.getScrollTop()
scrollLeft = editor.getScrollLeft()
lineHeightInPixels = editor.getLineHeight()
horizontalScrollbarHeight = editor.getHorizontalScrollbarHeight()
verticalScrollbarWidth = editor.getVerticalScrollbarWidth()
verticallyScrollable = editor.verticallyScrollable()
horizontallyScrollable = editor.horizontallyScrollable()
className = 'editor editor-colors react'
className += ' is-focused' if focused
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
GutterComponent {
editor, renderedRowRange, scrollTop, scrollHeight,
lineHeight: lineHeightInPixels, @pendingChanges
editor, renderedRowRange, maxLineNumberDigits, scrollTop, scrollHeight,
lineHeight: lineHeightInPixels, fontSize, fontFamily, @pendingChanges,
onWidthChanged: @onGutterWidthChanged
}
EditorScrollViewComponent {
@@ -55,6 +67,10 @@ EditorComponent = React.createClass
onScroll: @onVerticalScroll
scrollTop: scrollTop
scrollHeight: scrollHeight
visible: verticallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
scrollableInOppositeDirection: horizontallyScrollable
verticalScrollbarWidth: verticalScrollbarWidth
horizontalScrollbarHeight: horizontalScrollbarHeight
ScrollbarComponent
ref: 'horizontalScrollbar'
@@ -62,7 +78,19 @@ EditorComponent = React.createClass
orientation: 'horizontal'
onScroll: @onHorizontalScroll
scrollLeft: scrollLeft
scrollWidth: scrollWidth
scrollWidth: scrollWidth + @gutterWidth
visible: horizontallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
scrollableInOppositeDirection: verticallyScrollable
verticalScrollbarWidth: verticalScrollbarWidth
horizontalScrollbarHeight: horizontalScrollbarHeight
# Also used to measure the height/width of scrollbars after the initial render
ScrollbarCornerComponent
ref: 'scrollbarCorner'
visible: not @refreshingScrollbars and (@measuringScrollbars or horizontallyScrollable and verticallyScrollable)
measuringScrollbars: @measuringScrollbars
height: horizontalScrollbarHeight
width: verticalScrollbarWidth
getRenderedRowRange: ->
renderedRowRange = @props.editor.getVisibleRowRange()
@@ -86,6 +114,9 @@ EditorComponent = React.createClass
@observeEditor()
@listenForDOMEvents()
@listenForCommands()
@measureScrollbars()
@subscribe atom.themes, 'stylesheet-added stylsheet-removed', @onStylesheetsChanged
@subscribe scrollbarStyle.changes, @refreshScrollbars
@props.editor.setVisible(true)
@requestUpdate()
@@ -99,6 +130,8 @@ EditorComponent = React.createClass
componentDidUpdate: ->
@pendingChanges.length = 0
@cursorsMoved = false
@refreshingScrollbars = false
@measureScrollbars() if @measuringScrollbars
@props.parentView.trigger 'editor:display-updated'
observeEditor: ->
@@ -230,6 +263,16 @@ EditorComponent = React.createClass
@subscribe atom.config.observe 'editor.fontSize', @setFontSize
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
measureScrollbars: ->
@measuringScrollbars = false
{editor} = @props
scrollbarCornerNode = @refs.scrollbarCorner.getDOMNode()
width = (scrollbarCornerNode.offsetWidth - scrollbarCornerNode.clientWidth) or 15
height = (scrollbarCornerNode.offsetHeight - scrollbarCornerNode.clientHeight) or 15
editor.setVerticalScrollbarWidth(width)
editor.setHorizontalScrollbarHeight(height)
setFontSize: (fontSize) ->
@setState({fontSize})
@@ -285,6 +328,35 @@ EditorComponent = React.createClass
event.preventDefault()
onStylesheetsChanged: (stylesheet) ->
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
containsScrollbarSelector: (stylesheet) ->
for rule in stylesheet.cssRules
if rule.selectorText?.indexOf('scrollbar') > -1
return true
false
refreshScrollbars: ->
# Believe it or not, proper handling of changes to scrollbar styles requires
# three DOM updates.
# Scrollbar style changes won't apply to scrollbars that are already
# visible, so first we need to hide scrollbars so we can redisplay them and
# force Chromium to apply updates.
@refreshingScrollbars = true
@requestUpdate()
# Next, we display only the scrollbar corner so we can measure the new
# scrollbar dimensions. The ::measuringScrollbars property will be set back
# to false after the scrollbars are measured.
@measuringScrollbars = true
@requestUpdate()
# Finally, we restore the scrollbars based on the newly-measured dimensions
# if the editor's content and dimensions require them to be visible.
@requestUpdate()
clearPreservedRowRange: ->
@preservedRowRange = null
@scrollingVertically = false
@@ -312,10 +384,10 @@ EditorComponent = React.createClass
@requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
onScrollTopChanged: ->
@preservedRowRange = @getRenderedRowRange()
# @preservedRowRange = @getRenderedRowRange()
@scrollingVertically = true
@clearPreservedRowRangeAfterDelay ?= debounce(@clearPreservedRowRange, 200)
@clearPreservedRowRangeAfterDelay()
# @clearPreservedRowRangeAfterDelay ?= debounce(@clearPreservedRowRange, 200)
# @clearPreservedRowRangeAfterDelay()
@requestUpdate()
onSelectionRemoved: (selection) ->
@@ -325,6 +397,9 @@ EditorComponent = React.createClass
onCursorsMoved: ->
@cursorsMoved = true
onGutterWidthChanged: (@gutterWidth) ->
@requestUpdate()
requestUpdate: ->
if @batchingUpdates
@updateRequested = true
+11 -9
Ver Arquivo
@@ -25,9 +25,9 @@ EditorScrollViewComponent = React.createClass
inputStyle.WebkitTransform = 'translateZ(0)'
contentStyle =
height: scrollHeight
# height: scrollHeight
minWidth: scrollWidth
WebkitTransform: "translate3d(#{-editor.getScrollLeft()}px, #{-editor.getScrollTop()}px, 0)"
# WebkitTransform: "translate3d(#{-editor.getScrollLeft()}px, #{-editor.getScrollTop()}px, 0px)"
div className: 'scroll-view',
InputComponent
@@ -42,7 +42,7 @@ EditorScrollViewComponent = React.createClass
CursorsComponent({editor, cursorsMoved, cursorBlinkPeriod, cursorBlinkResumeDelay})
LinesComponent {
ref: 'lines', editor, fontSize, fontFamily, lineHeight, showIndentGuide,
renderedRowRange, pendingChanges, scrollingVertically
renderedRowRange, pendingChanges, scrollingVertically, scrollTop: editor.getScrollTop()
}
div className: 'underlayer',
SelectionsComponent({editor})
@@ -182,17 +182,19 @@ EditorScrollViewComponent = React.createClass
measureHeightAndWidth: ->
return unless @isMounted()
node = @getDOMNode()
computedStyle = getComputedStyle(node)
{editor} = @props
node = @getDOMNode()
editorNode = node.parentNode
{position} = getComputedStyle(editorNode)
{width, height} = editorNode.style
unless computedStyle.height is '0px'
clientHeight = node.clientHeight
if position is 'absolute' or height
clientHeight = node.clientHeight
editor.setHeight(clientHeight) if clientHeight > 0
unless computedStyle.width is '0px'
if position is 'absolute' or width
clientWidth = node.clientWidth
editor.setWidth(clientWidth) if clientHeight > 0
editor.setWidth(clientWidth) if clientWidth > 0
focus: ->
@refs.input.focus()
+12
Ver Arquivo
@@ -1838,6 +1838,8 @@ class Editor extends Model
setHeight: (height) -> @displayBuffer.setHeight(height)
getHeight: -> @displayBuffer.getHeight()
getClientHeight: -> @displayBuffer.getClientHeight()
setWidth: (width) -> @displayBuffer.setWidth(width)
getWidth: -> @displayBuffer.getWidth()
@@ -1876,6 +1878,16 @@ class Editor extends Model
scrollToBufferPosition: (bufferPosition) -> @displayBuffer.scrollToBufferPosition(bufferPosition)
horizontallyScrollable: -> @displayBuffer.horizontallyScrollable()
verticallyScrollable: -> @displayBuffer.verticallyScrollable()
getHorizontalScrollbarHeight: -> @displayBuffer.getHorizontalScrollbarHeight()
setHorizontalScrollbarHeight: (height) -> @displayBuffer.setHorizontalScrollbarHeight(height)
getVerticalScrollbarWidth: -> @displayBuffer.getVerticalScrollbarWidth()
setVerticalScrollbarWidth: (width) -> @displayBuffer.setVerticalScrollbarWidth(width)
# Deprecated: Call {::joinLines} instead.
joinLine: ->
deprecate("Use Editor::joinLines() instead")
+22 -13
Ver Arquivo
@@ -8,34 +8,36 @@ GutterComponent = React.createClass
displayName: 'GutterComponent'
mixins: [SubscriberMixin]
lastMeasuredWidth: null
render: ->
div className: 'gutter',
@renderLineNumbers() if @isMounted()
renderLineNumbers: ->
{editor, renderedRowRange, scrollTop, scrollHeight} = @props
{editor, renderedRowRange, maxLineNumberDigits, scrollTop, scrollHeight} = @props
[startRow, endRow] = renderedRowRange
charWidth = editor.getDefaultCharWidth()
lineHeight = editor.getLineHeight()
maxDigits = editor.getLastBufferRow().toString().length
style =
width: charWidth * (maxDigits + 1.5)
width: charWidth * (maxLineNumberDigits + 1.5)
height: scrollHeight
WebkitTransform: "translate3d(0, #{-scrollTop}px, 0)"
WebkitTransform: "translate3d(0px, #{-scrollTop}px, 0px)"
lineNumbers = []
tokenizedLines = editor.linesForScreenRows(startRow, endRow - 1)
tokenizedLines.push({id: 0}) if tokenizedLines.length is 0
wrapCount = 0
for bufferRow, i in editor.bufferRowsForScreenRows(startRow, endRow - 1)
if bufferRow is lastBufferRow
lineNumber = ''
key = "#{bufferRow + 1}-#{++wrapCount}"
else
lastBufferRow = bufferRow
wrapCount = 0
lineNumber = (bufferRow + 1).toString()
key = lineNumber
key = tokenizedLines[i].id
screenRow = startRow + i
lineNumbers.push(LineNumberComponent({key, lineNumber, maxDigits, bufferRow, screenRow, lineHeight}))
lineNumbers.push(LineNumberComponent({key, lineNumber, maxLineNumberDigits, bufferRow, screenRow, lineHeight}))
lastBufferRow = bufferRow
div className: 'line-numbers', style: style,
@@ -45,7 +47,7 @@ GutterComponent = React.createClass
# non-zero-delta change to the screen lines has occurred within the current
# visible row range.
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeight')
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeight', 'fontSize')
{renderedRowRange, pendingChanges} = newProps
for change in pendingChanges when change.screenDelta > 0 or change.bufferDelta > 0
@@ -53,6 +55,13 @@ GutterComponent = React.createClass
false
componentDidUpdate: (oldProps) ->
unless @lastMeasuredWidth? and isEqualForProperties(oldProps, @props, 'maxLineNumberDigits', 'fontSize', 'fontFamily')
width = @getDOMNode().offsetWidth
if width isnt @lastMeasuredWidth
@lastMeasuredWidth = width
@props.onWidthChanged(width)
LineNumberComponent = React.createClass
displayName: 'LineNumberComponent'
@@ -66,9 +75,9 @@ LineNumberComponent = React.createClass
dangerouslySetInnerHTML: {__html: @buildInnerHTML()}
buildInnerHTML: ->
{lineNumber, maxDigits} = @props
if lineNumber.length < maxDigits
padding = multiplyString('&nbsp;', maxDigits - lineNumber.length)
{lineNumber, maxLineNumberDigits} = @props
if lineNumber.length < maxLineNumberDigits
padding = multiplyString('&nbsp;', maxLineNumberDigits - lineNumber.length)
padding + lineNumber + @iconDivHTML
else
lineNumber + @iconDivHTML
@@ -76,4 +85,4 @@ LineNumberComponent = React.createClass
iconDivHTML: '<div class="icon-right"></div>'
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'lineHeight', 'screenRow')
not isEqualForProperties(newProps, @props, 'lineHeight', 'screenRow', 'maxLineNumberDigits')
+7 -8
Ver Arquivo
@@ -12,12 +12,13 @@ LinesComponent = React.createClass
render: ->
if @isMounted()
{editor, renderedRowRange, lineHeight, showIndentGuide} = @props
{editor, renderedRowRange, scrollTop, lineHeight, showIndentGuide} = @props
[startRow, endRow] = renderedRowRange
offset = scrollTop % lineHeight
lines =
for tokenizedLine, i in editor.linesForScreenRows(startRow, endRow - 1)
LineComponent({key: tokenizedLine.id, tokenizedLine, showIndentGuide, lineHeight, screenRow: startRow + i})
LineComponent({key: tokenizedLine.id, index: i, offset, tokenizedLine, showIndentGuide, lineHeight, screenRow: startRow + i})
div {className: 'lines'}, lines
@@ -28,7 +29,7 @@ LinesComponent = React.createClass
@measureLineHeightAndCharWidth()
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'showIndentGuide')
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'fontSize', 'fontFamily', 'lineHeight', 'showIndentGuide')
{renderedRowRange, pendingChanges} = newProps
for change in pendingChanges
@@ -103,11 +104,9 @@ LineComponent = React.createClass
displayName: 'LineComponent'
render: ->
{screenRow, lineHeight} = @props
{index, screenRow, lineHeight, offset} = @props
style =
top: screenRow * lineHeight
position: 'absolute'
WebkitTransform: "translate3d(0px, #{index * lineHeight - offset}px, 0px)"
div className: 'line', style: style, 'data-screen-row': screenRow, dangerouslySetInnerHTML: {__html: @buildInnerHTML()}
@@ -137,4 +136,4 @@ LineComponent = React.createClass
"<span>#{scopeTree.getValueAsHtml({hasIndentGuide: @props.showIndentGuide})}</span>"
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'showIndentGuide', 'lineHeight', 'screenRow')
not isEqualForProperties(newProps, @props, 'showIndentGuide', 'lineHeight', 'screenRow', 'index', 'offset')
+18 -5
Ver Arquivo
@@ -1,13 +1,24 @@
React = require 'react'
{div} = require 'reactionary'
{isEqualForProperties} = require 'underscore-plus'
{extend, isEqualForProperties} = require 'underscore-plus'
module.exports =
ScrollbarComponent = React.createClass
render: ->
{orientation, className, scrollHeight, scrollWidth} = @props
{orientation, className, scrollHeight, scrollWidth, visible} = @props
{scrollableInOppositeDirection, horizontalScrollbarHeight, verticalScrollbarWidth} = @props
div {className, @onScroll},
style = {}
style.display = 'none' unless visible
switch orientation
when 'vertical'
style.width = verticalScrollbarWidth
style.bottom = horizontalScrollbarHeight if scrollableInOppositeDirection
when 'horizontal'
style.height = horizontalScrollbarHeight
style.right = verticalScrollbarWidth if scrollableInOppositeDirection
div {className, style, @onScroll},
switch orientation
when 'vertical'
div className: 'scrollbar-content', style: {height: scrollHeight}
@@ -21,11 +32,13 @@ ScrollbarComponent = React.createClass
throw new Error("Must specify an orientation property of 'vertical' or 'horizontal'")
shouldComponentUpdate: (newProps) ->
return true if newProps.visible isnt @props.visible
switch @props.orientation
when 'vertical'
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop')
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop', 'scrollableInOppositeDirection')
when 'horizontal'
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft')
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft', 'scrollableInOppositeDirection')
componentDidUpdate: ->
{orientation, scrollTop, scrollLeft} = @props
+18
Ver Arquivo
@@ -0,0 +1,18 @@
React = require 'react'
{div} = require 'reactionary'
module.exports =
ScrollbarComponent = React.createClass
render: ->
{visible, measuringScrollbars, width, height} = @props
if measuringScrollbars
height = 25
width = 25
display = 'none' unless visible
div className: 'scrollbar-corner', style: {display, width, height},
div style:
height: height + 1
width: width + 1
+16 -8
Ver Arquivo
@@ -158,10 +158,10 @@ class ThemeManager
# load path.
#
# Returns the absolute path to the required stylesheet.
requireStylesheet: (stylesheetPath, ttype = 'bundled', htmlElement) ->
requireStylesheet: (stylesheetPath, type = 'bundled', htmlElement) ->
if fullPath = @resolveStylesheet(stylesheetPath)
content = @loadStylesheet(fullPath)
@applyStylesheet(fullPath, content, ttype = 'bundled', htmlElement)
@applyStylesheet(fullPath, content, type = 'bundled', htmlElement)
else
throw new Error("Could not find a file at path '#{stylesheetPath}'")
@@ -192,16 +192,24 @@ class ThemeManager
removeStylesheet: (stylesheetPath) ->
fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath
@stylesheetElementForId(@stringToId(fullPath)).remove()
@emit 'stylesheets-changed'
element = @stylesheetElementForId(@stringToId(fullPath))
if element.length > 0
stylesheet = element[0].sheet
element.remove()
@emit 'stylesheet-removed', stylesheet
@emit 'stylesheets-changed'
applyStylesheet: (path, text, ttype = 'bundled', htmlElement=$('html')) ->
applyStylesheet: (path, text, type = 'bundled', htmlElement=$('html')) ->
styleElement = @stylesheetElementForId(@stringToId(path), htmlElement)
if styleElement.length
@emit 'stylesheet-removed', styleElement[0].sheet
styleElement.text(text)
else
if htmlElement.find("head style.#{ttype}").length
htmlElement.find("head style.#{ttype}:last").after "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
styleElement = $("<style class='#{type}' id='#{@stringToId(path)}'>#{text}</style>")
if htmlElement.find("head style.#{type}").length
htmlElement.find("head style.#{type}:last").after(styleElement)
else
htmlElement.find("head").append "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
htmlElement.find("head").append(styleElement)
@emit 'stylesheet-added', styleElement[0].sheet
@emit 'stylesheets-changed'
+15
Ver Arquivo
@@ -14,6 +14,10 @@
.lines {
z-index: -1;
.line {
position: absolute;
}
}
.horizontal-scrollbar {
@@ -32,6 +36,17 @@
}
}
.vertical-scrollbar {
overflow-x: hidden;
}
.scrollbar-corner {
position: absolute;
overflow: auto;
bottom: 0;
right: 0;
}
.scroll-view {
overflow: hidden;
}