Add onDidStopChangingActivePaneItem for async callbacks

`onDidChangeActivePaneItem` is called synchronously when the active pane
item changes, and several non-critical actions preform work on that
event. Critical UI feedback, like changing the active tab, needs to
happen synchronously, but most other functionality should be run
asynchronously.
This commit is contained in:
Ross Allen
2015-10-02 10:57:28 -07:00
parent 69d9da1fa5
commit 41984a2317
3 changed files with 66 additions and 1 deletions

View File

@@ -122,6 +122,29 @@ describe "PaneContainer", ->
pane2.activate()
expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)]
describe "::onDidStopChangingActivePaneItem()", ->
[container, pane1, pane2, observed] = []
beforeEach ->
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
container.getRoot().splitRight(items: [new Object, new Object])
[pane1, pane2] = container.getPanes()
observed = []
container.onDidStopChangingActivePaneItem (item) -> observed.push(item)
it "invokes observers when the active item of the active pane stops changing", ->
pane2.activateNextItem()
pane2.activateNextItem()
advanceClock(100)
expect(observed).toEqual [pane2.itemAtIndex(0)]
it "invokes observers when the active pane stops changing", ->
pane1.activate()
pane2.activate()
advanceClock(100)
expect(observed).toEqual [pane2.itemAtIndex(0)]
describe "::observePanes()", ->
it "invokes observers with all current and future panes", ->
container = new PaneContainer

View File

@@ -12,6 +12,8 @@ class PaneContainer extends Model
@version: 1
root: null
stoppedChangingActivePaneItemDelay: 100
stoppedChangingActivePaneItemTimeout: null
@deserialize: (state) ->
container = Object.create(@prototype) # allows us to pass a self reference to our child before invoking constructor
@@ -82,6 +84,9 @@ class PaneContainer extends Model
onDidChangeActivePaneItem: (fn) ->
@emitter.on 'did-change-active-pane-item', fn
onDidStopChangingActivePaneItem: (fn) ->
@emitter.on 'did-stop-changing-active-pane-item', fn
observeActivePaneItem: (fn) ->
fn(@getActivePaneItem())
@onDidChangeActivePaneItem(fn)
@@ -189,12 +194,18 @@ class PaneContainer extends Model
# Called by Model superclass when destroyed
destroyed: ->
@cancelStoppedChangingActivePaneItemTimeout()
pane.destroy() for pane in @getPanes()
@subscriptions.dispose()
@emitter.dispose()
cancelStoppedChangingActivePaneItemTimeout: ->
if @stoppedChangingActivePaneItemTimeout?
clearTimeout(@stoppedChangingActivePaneItemTimeout)
monitorActivePaneItem: ->
childSubscription = null
@subscriptions.add @observeActivePane (activePane) =>
if childSubscription?
@subscriptions.remove(childSubscription)
@@ -202,6 +213,14 @@ class PaneContainer extends Model
childSubscription = activePane.observeActiveItem (activeItem) =>
@emitter.emit 'did-change-active-pane-item', activeItem
@cancelStoppedChangingActivePaneItemTimeout()
stoppedChangingActivePaneItemCallback = =>
@stoppedChangingActivePaneItemTimeout = null
@emitter.emit 'did-stop-changing-active-pane-item', activeItem
@stoppedChangingActivePaneItemTimeout =
setTimeout(
stoppedChangingActivePaneItemCallback,
@stoppedChangingActivePaneItemDelay)
@subscriptions.add(childSubscription)

View File

@@ -209,11 +209,34 @@ class Workspace extends Model
# Essential: Invoke the given callback when the active pane item changes.
#
# Because observers are invoked synchronously, it's important not to perform
# any expensive operations via this method. Consider
# {::onDidStopChangingActivePaneItem} to delay operations until after changes
# stop occurring.
#
# * `callback` {Function} to be called when the active pane item changes.
# * `item` The active pane item.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback)
onDidChangeActivePaneItem: (callback) ->
@paneContainer.onDidChangeActivePaneItem(callback)
# Essential: Invoke the given callback when the active pane item stops
# changing.
#
# Observers are called asynchronously 100ms after the last active pane item
# change. Handling changes here rather than in the synchronous
# {::onDidChangeActivePaneItem} prevents unneeded work if the user is quickly
# changing or closing tabs and ensures critical UI feedback, like changing the
# highlighted tab, gets priority over work that can be done asynchronously.
#
# * `callback` {Function} to be called when the active pane item stopts
# changing.
# * `item` The active pane item.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidStopChangingActivePaneItem: (callback) ->
@paneContainer.onDidStopChangingActivePaneItem(callback)
# Essential: Invoke the given callback with the current active pane item and
# with all future active pane items in the workspace.