At the model layer: Focus next pane when a focused pane is destroyed

This incorporates the Focusable mixin into PaneModel and ensures that
all panes in the same pane container share a single focus context.
This commit is contained in:
Nathan Sobo
2014-01-09 10:41:48 -07:00
parent 4e99d003ee
commit 2317c6835e
4 changed files with 70 additions and 11 deletions

View File

@@ -80,7 +80,7 @@ describe "PaneModel", ->
it "focuses the new pane, even if the current pane isn't focused", ->
expect(pane1.focused).toBe false
pane2 = pane1.split()
pane2 = pane1.splitRight()
expect(pane2.focused).toBe true
describe "::destroy()", ->
@@ -110,3 +110,13 @@ describe "PaneModel", ->
expect(container.root.children).toEqual [pane1, pane2]
pane2.destroy()
expect(container.root).toBe pane1
describe "if the pane is focused", ->
it "shifts focus to the next pane", ->
pane2 = pane1.splitRight()
pane3 = pane2.splitRight()
pane2.focus()
expect(pane2.focused).toBe true
expect(pane3.focused).toBe false
pane2.destroy()
expect(pane3.focused).toBe true

View File

@@ -1,4 +1,6 @@
{Model, Sequence} = require 'theorist'
{flatten} = require 'underscore-plus'
Delegator = require 'delegato'
Serializable = require 'serializable'
PaneRow = null
@@ -8,15 +10,24 @@ module.exports =
class PaneAxisModel extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
Delegator.includeInto(this)
@delegatesMethod 'focusNextPane', toProperty: 'parent'
@property 'focusContext'
constructor: ({@orientation, children}) ->
@children = Sequence.fromArray(children ? [])
@children.onEach (child) =>
@subscribe @$focusContext, (focusContext) =>
child.focusContext = focusContext for child in @children
@subscribe @children.onEach (child) =>
child.parent = this
child.focusContext = @focusContext
@subscribe child, 'destroyed', => @removeChild(child)
@children.onRemoval (child) => @unsubscribe(child)
@subscribe @children.onRemoval (child) => @unsubscribe(child)
@when @children.$length.becomesLessThan(2), 'reparentLastChild'
@when @children.$length.becomesLessThan(1), 'destroy'
@@ -34,6 +45,9 @@ class PaneAxisModel extends Model
else
PaneRow ?= require './pane-row'
getPanes: ->
flatten(@children.map (child) -> child.getPanes())
addChild: (child, index=@children.length) ->
@children.splice(index, 0, child)

View File

@@ -1,5 +1,7 @@
{Model} = require 'theorist'
Serializable = require 'serializable'
{find} = require 'underscore-plus'
FocusContext = require './focus-context'
module.exports =
class PaneContainerModel extends Model
@@ -8,10 +10,13 @@ class PaneContainerModel extends Model
@properties
root: null
focusContext: -> new FocusContext
constructor: ->
super
@subscribe @$root, (root) => root?.parent = this
@subscribe @$root.filterDefined(), (root) =>
root.parent = this
root.focusContext = @focusContext
deserializeParams: (params) ->
params.root = atom.deserializers.deserialize(params.root)
@@ -23,3 +28,30 @@ class PaneContainerModel extends Model
replaceChild: (oldChild, newChild) ->
throw new Error("Replacing non-existent child") if oldChild isnt @root
@root = newChild
getPanes: ->
@root?.getPanes() ? []
getFocusedPane: ->
find @getPanes(), (pane) -> pane.focused
focusNextPane: ->
panes = @getPanes()
if panes.length > 1
currentIndex = panes.indexOf(@getFocusedPane())
nextIndex = (currentIndex + 1) % panes.length
panes[nextIndex].focus()
true
else
false
focusPreviousPane: ->
panes = @getPanes()
if panes.length > 1
currentIndex = panes.indexOf(@getFocusedPane())
previousIndex = currentIndex - 1
previousIndex = panes.length - 1 if previousIndex < 0
panes[previousIndex].focus()
true
else
false

View File

@@ -3,17 +3,18 @@
{Model} = require 'theorist'
Serializable = require 'serializable'
PaneAxisModel = require './pane-axis-model'
Focusable = require './focusable'
Pane = null
module.exports =
class PaneModel extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
Focusable.includeInto(this)
@properties
items: -> []
activeItem: null
focused: false
constructor: ->
super
@@ -32,11 +33,7 @@ class PaneModel extends Model
getViewClass: -> Pane ?= require './pane'
focus: ->
@focused = true
blur: ->
@focused = false
getPanes: -> [this]
# Public: Returns all contained views.
getItems: ->
@@ -132,6 +129,10 @@ class PaneModel extends Model
destroyInactiveItems: ->
@destroyItem(item) for item in @getItems() when item isnt @activeItem
# Private: Called by model superclass
destroyed: ->
@parent.focusNextPane() if @focused
# Public: Prompt the user to save the given item.
promptToSaveItem: (item) ->
return true unless item.shouldPromptToSave?()
@@ -211,8 +212,10 @@ class PaneModel extends Model
if @parent.orientation isnt orientation
@parent.replaceChild(this, new PaneAxisModel({orientation, children: [this]}))
newPane = new @constructor(extend({focused: true}, params))
newPane = new @constructor(params)
switch side
when 'before' then @parent.insertChildBefore(this, newPane)
when 'after' then @parent.insertChildAfter(this, newPane)
newPane.focus()
newPane