diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 74186190d..eaaf48394 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -223,11 +223,30 @@ describe "TextEditorPresenter", -> describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> - presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10) + presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 20) expect(presenter.state.verticalScrollbar.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) expect(presenter.state.verticalScrollbar.scrollTop).toBe 50 + it "never exceeds the computed scroll height minus the computed client height", -> + presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(100) + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> presenter.setHeight(60) + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(15) + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + # Scroll top only gets smaller when needed as dimensions change, never bigger + scrollTopBefore = presenter.state.verticalScrollbar.scrollTop + expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') + expect(presenter.state.verticalScrollbar.scrollTop).toBe scrollTopBefore + describe ".content", -> describe ".scrollingVertically", -> it "is true for ::stoppedScrollingDelay milliseconds following a changes to ::scrollTop", -> @@ -323,11 +342,30 @@ describe "TextEditorPresenter", -> describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> - presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10) + presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 20) expect(presenter.state.content.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) expect(presenter.state.content.scrollTop).toBe 50 + it "never exceeds the computed scroll height minus the computed client height", -> + presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(100) + expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> presenter.setHeight(60) + expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(15) + expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) + expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + # Scroll top only gets smaller when needed as dimensions change, never bigger + scrollTopBefore = presenter.state.verticalScrollbar.scrollTop + expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') + expect(presenter.state.content.scrollTop).toBe scrollTopBefore + describe ".scrollLeft", -> it "tracks the value of ::scrollLeft", -> presenter = new TextEditorPresenter(model: editor, scrollLeft: 10, lineHeight: 10, lineOverdrawMargin: 1) @@ -1366,11 +1404,30 @@ describe "TextEditorPresenter", -> describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> - presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10) + presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 20) expect(presenter.state.gutter.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) expect(presenter.state.gutter.scrollTop).toBe 50 + it "never exceeds the computed scroll height minus the computed client height", -> + presenter = new TextEditorPresenter(model: editor, scrollTop: 10, lineHeight: 10, height: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(100) + expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> presenter.setHeight(60) + expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(15) + expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) + expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + + # Scroll top only gets smaller when needed as dimensions change, never bigger + scrollTopBefore = presenter.state.verticalScrollbar.scrollTop + expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') + expect(presenter.state.gutter.scrollTop).toBe scrollTopBefore + describe ".backgroundColor", -> it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", -> presenter = new TextEditorPresenter(model: editor, backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)") diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 7e88c0a87..6bb65ab3f 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -96,7 +96,7 @@ class TextEditorPresenter @state.gutter.scrollHeight = scrollHeight @state.verticalScrollbar.scrollHeight = scrollHeight - scrollTop = @scrollTop + scrollTop = @computeScrollTop() @state.content.scrollTop = scrollTop @state.gutter.scrollTop = scrollTop @state.verticalScrollbar.scrollTop = scrollTop @@ -114,26 +114,16 @@ class TextEditorPresenter @emitter.emit 'did-update-state' updateScrollbarsState: -> - contentWidth = @computeContentWidth() - contentHeight = @computeContentHeight() - clientWidthWithoutVerticalScrollbar = @contentFrameWidth - clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth - clientHeightWithoutHorizontalScrollbar = @getHeight() - clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight - horizontalScrollbarVisible = - contentWidth > clientWidthWithoutVerticalScrollbar or - contentWidth > clientWidthWithVerticalScrollbar and contentHeight > clientHeightWithoutHorizontalScrollbar - verticalScrollbarVisible = - contentHeight > clientHeightWithoutHorizontalScrollbar or - contentHeight > clientHeightWithHorizontalScrollbar and contentWidth > clientWidthWithoutVerticalScrollbar + horizontalScrollbarHeight = @computeHorizontalScrollbarHeight() + verticalScrollbarWidth = @computeVerticalScrollbarWidth() - @state.horizontalScrollbar.visible = horizontalScrollbarVisible + @state.horizontalScrollbar.visible = horizontalScrollbarHeight > 0 @state.horizontalScrollbar.height = @horizontalScrollbarHeight - @state.horizontalScrollbar.right = if verticalScrollbarVisible then @verticalScrollbarWidth else 0 + @state.horizontalScrollbar.right = verticalScrollbarWidth - @state.verticalScrollbar.visible = verticalScrollbarVisible + @state.verticalScrollbar.visible = verticalScrollbarWidth > 0 @state.verticalScrollbar.width = @verticalScrollbarWidth - @state.verticalScrollbar.bottom = if horizontalScrollbarVisible then @horizontalScrollbarHeight else 0 + @state.verticalScrollbar.bottom = horizontalScrollbarHeight @emitter.emit 'did-update-state' @@ -324,11 +314,11 @@ class TextEditorPresenter regions computeStartRow: -> - startRow = Math.floor(@scrollTop / @lineHeight) - @lineOverdrawMargin + startRow = Math.floor(@computeScrollTop() / @lineHeight) - @lineOverdrawMargin Math.max(0, startRow) computeEndRow: -> - startRow = Math.floor(@scrollTop / @lineHeight) + startRow = Math.floor(@computeScrollTop() / @lineHeight) visibleLinesCount = Math.ceil(@getHeight() / @lineHeight) + 1 endRow = startRow + visibleLinesCount + @lineOverdrawMargin Math.min(@model.getScreenLineCount(), endRow) @@ -347,6 +337,49 @@ class TextEditorPresenter computeContentHeight: -> @lineHeight * @model.getScreenLineCount() + computeClientHeight: -> + @getHeight() - @computeHorizontalScrollbarHeight() + + computeClientWidth: -> + @contentFrameWidth - @computeVerticalScrollbarWidth() + + computeScrollTop: -> + @scrollTop = Math.min(@scrollTop, @computeScrollHeight() - @computeClientHeight()) + + computeHorizontalScrollbarHeight: -> + contentWidth = @computeContentWidth() + contentHeight = @computeContentHeight() + clientWidthWithoutVerticalScrollbar = @contentFrameWidth + clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth + clientHeightWithoutHorizontalScrollbar = @getHeight() + clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight + + horizontalScrollbarVisible = + contentWidth > clientWidthWithoutVerticalScrollbar or + contentWidth > clientWidthWithVerticalScrollbar and contentHeight > clientHeightWithoutHorizontalScrollbar + + if horizontalScrollbarVisible + @horizontalScrollbarHeight + else + 0 + + computeVerticalScrollbarWidth: -> + contentWidth = @computeContentWidth() + contentHeight = @computeContentHeight() + clientWidthWithoutVerticalScrollbar = @contentFrameWidth + clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth + clientHeightWithoutHorizontalScrollbar = @getHeight() + clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight + + verticalScrollbarVisible = + contentHeight > clientHeightWithoutHorizontalScrollbar or + contentHeight > clientHeightWithHorizontalScrollbar and contentWidth > clientWidthWithoutVerticalScrollbar + + if verticalScrollbarVisible + @verticalScrollbarWidth + else + 0 + lineDecorationClassesForRow: (row) -> return null if @model.isMini()