diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 2fd213cd2..92a7e5131 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -6,6 +6,10 @@ describe "TextEditorPresenter", -> [buffer, editor] = [] beforeEach -> + # These *should* be mocked in the spec helper, but changing that now would break packages :-( + spyOn(window, "setInterval").andCallFake window.fakeSetInterval + spyOn(window, "clearInterval").andCallFake window.fakeClearInterval + buffer = new TextBuffer(filePath: require.resolve('./fixtures/sample.js')) editor = new TextEditor({buffer}) waitsForPromise -> buffer.load() @@ -519,3 +523,44 @@ describe "TextEditorPresenter", -> editor.setCursorBufferPosition([1, Infinity]) presenter = new TextEditorPresenter(model: editor, clientHeight: 20, scrollTop: 0, lineHeight: 10, lineOverdrawMargin: 0, baseCharacterWidth: 10) expect(stateForCursor(presenter, 0).width).toBe 10 + + describe ".blinkOff", -> + it "alternates between true and false twice per ::cursorBlinkPeriod", -> + cursorBlinkPeriod = 100 + cursorBlinkResumeDelay = 200 + presenter = new TextEditorPresenter({model: editor, cursorBlinkPeriod, cursorBlinkResumeDelay}) + + expect(presenter.state.content.blinkCursorsOff).toBe false + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe true + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe false + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe true + + it "stops alternating for ::cursorBlinkResumeDelay when a cursor moves or a cursor is added", -> + cursorBlinkPeriod = 100 + cursorBlinkResumeDelay = 200 + presenter = new TextEditorPresenter({model: editor, cursorBlinkPeriod, cursorBlinkResumeDelay}) + + expect(presenter.state.content.blinkCursorsOff).toBe false + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe true + + editor.moveRight() + expect(presenter.state.content.blinkCursorsOff).toBe false + + advanceClock(cursorBlinkResumeDelay) + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe true + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe false + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe true + + editor.addCursorAtBufferPosition([1, 0]) + expect(presenter.state.content.blinkCursorsOff).toBe false + + advanceClock(cursorBlinkResumeDelay) + advanceClock(cursorBlinkPeriod / 2) + expect(presenter.state.content.blinkCursorsOff).toBe true diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index e4a680b26..e4c41f268 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1,14 +1,19 @@ {CompositeDisposable} = require 'event-kit' {Point} = require 'text-buffer' +_ = require 'underscore-plus' module.exports = class TextEditorPresenter - constructor: ({@model, @clientHeight, @clientWidth, @scrollTop, @scrollLeft, @lineHeight, @baseCharacterWidth, @lineOverdrawMargin}) -> + toggleCursorBlinkHandle: null + startBlinkingCursorsAfterDelay: null + + constructor: ({@model, @clientHeight, @clientWidth, @scrollTop, @scrollLeft, @lineHeight, @baseCharacterWidth, @lineOverdrawMargin, @cursorBlinkPeriod, @cursorBlinkResumeDelay}) -> @disposables = new CompositeDisposable @charWidthsByScope = {} @observeModel() @observeConfig() @buildState() + @startBlinkingCursors() destroy: -> @disposables.dispose() @@ -41,6 +46,7 @@ class TextEditorPresenter @updateLinesState() buildCursorsState: -> + @state.content.blinkCursorsOff = false @state.content.cursors = {} @updateCursorsState() @@ -145,6 +151,10 @@ class TextEditorPresenter decorationClasses + getCursorBlinkPeriod: -> @cursorBlinkPeriod + + getCursorBlinkResumeDelay: -> @cursorBlinkResumeDelay + setScrollTop: (@scrollTop) -> @updateContentState() @updateLinesState() @@ -275,8 +285,12 @@ class TextEditorPresenter @updateLinesState() observeCursor: (cursor) -> - didChangePositionDisposable = cursor.onDidChangePosition(@updateCursorsState.bind(this)) + didChangePositionDisposable = cursor.onDidChangePosition => + @pauseCursorBlinking() + @updateCursorsState() + didChangeVisibilityDisposable = cursor.onDidChangeVisibility(@updateCursorsState.bind(this)) + didDestroyDisposable = cursor.onDidDestroy => @disposables.remove(didChangePositionDisposable) @disposables.remove(didChangeVisibilityDisposable) @@ -289,4 +303,20 @@ class TextEditorPresenter didAddCursor: (cursor) -> @observeCursor(cursor) + @pauseCursorBlinking() @updateCursorsState() + + startBlinkingCursors: -> + @toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2) + + stopBlinkingCursors: -> + clearInterval(@toggleCursorBlinkHandle) + + toggleCursorBlink: -> + @state.content.blinkCursorsOff = not @state.content.blinkCursorsOff + + pauseCursorBlinking: -> + @state.content.blinkCursorsOff = false + @stopBlinkingCursors() + @startBlinkingCursorsAfterDelay ?= _.debounce(@startBlinkingCursors, @getCursorBlinkResumeDelay()) + @startBlinkingCursorsAfterDelay()