Remove pane splitting/unsplitting logic from view

There's still some failing specs around focus management, but it's
getting closer.
This commit is contained in:
Nathan Sobo
2014-01-08 18:49:50 -07:00
parent f031a9706d
commit 8efcb1abfa
6 changed files with 88 additions and 108 deletions

View File

@@ -1,7 +1,14 @@
{Model, Sequence} = require 'theorist'
Serializable = require 'serializable'
PaneRow = null
PaneColumn = null
module.exports =
class PaneAxisModel extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
constructor: ({@orientation, children}) ->
@children = Sequence.fromArray(children ? [])
@@ -14,6 +21,19 @@ class PaneAxisModel extends Model
@when @children.$length.becomesLessThan(2), 'reparentLastChild'
@when @children.$length.becomesLessThan(1), 'destroy'
deserializeParams: (params) ->
params.children = params.children.map (childState) -> atom.deserializers.deserialize(childState)
params
serializeParams: ->
children: @children.map (child) -> child.serialize()
getViewClass: ->
if @orientation is 'vertical'
PaneColumn ?= require './pane-column'
else
PaneRow ?= require './pane-row'
addChild: (child, index=@children.length) ->
@children.splice(index, 0, child)

View File

@@ -1,68 +1,36 @@
Serializable = require 'serializable'
{$, View} = require './space-pen-extensions'
PaneAxisModel = require './pane-axis-model'
Pane = null
### Internal ###
module.exports =
class PaneAxis extends View
Serializable.includeInto(this)
initialize: (@model) ->
@subscribe @model.children.onRemoval @onChildRemoved
@subscribe @model.children.onEach @onChildAdded
initialize: ({children}={}) ->
@model = new PaneAxisModel
@model.children.on 'changed', ({index, removedValues, insertedValues}) =>
@onChildRemoved(child, index) for child in removedValues
@onChildAdded(child, index) for child in insertedValues
@onChildAdded(child) for child in children ? []
@addChild(child) for child in children ? []
serializeParams: ->
children: @children().views().map (child) -> child.serialize()
deserializeParams: (params) ->
params.children = params.children.map (childState) -> atom.deserializers.deserialize(childState)
params
viewForModel: (model) ->
viewClass = model.getViewClass()
model._view ?= new viewClass(model)
addChild: (child, index) ->
@model.addChild(child, index)
@model.addChild(child.model, index)
removeChild: (child) ->
@model.removeChild(child)
@model.removeChild(child.model)
onChildAdded: (child, index) =>
@insertAt(index, child)
view = @viewForModel(child)
@insertAt(index, view)
onChildRemoved: (child) =>
parent = @parent().view()
container = @getContainer()
childWasInactive = not child.isActive?()
primitiveRemove = (child) =>
node = child[0]
$.cleanData(node.getElementsByTagName('*'))
$.cleanData([node])
this[0].removeChild(node)
# use primitive .removeChild() dom method instead of .remove() to avoid recursive loop
if @children().length == 2
primitiveRemove(child)
sibling = @children().view()
siblingFocused = sibling.is(':has(:focus)')
sibling.detach()
if parent.setRoot?
parent.setRoot(sibling, suppressPaneItemChangeEvents: childWasInactive)
else
parent.insertChildBefore(this, sibling)
parent.removeChild(this)
sibling.focus() if siblingFocused
else
primitiveRemove(child)
Pane = require './pane'
container.trigger 'pane:removed', [child] if child instanceof Pane
detachChild: (child) ->
child.detach()
view = @viewForModel(child)
view.detach()
Pane ?= require './pane'
@getContainer()?.trigger 'pane:removed', [view] if view instanceof Pane
getContainer: ->
@closest('.panes').view()

View File

@@ -1,7 +1,11 @@
{Model} = require 'theorist'
Serializable = require 'serializable'
module.exports =
class PaneContainerModel extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
@properties
root: null
@@ -9,6 +13,13 @@ class PaneContainerModel extends Model
super
@subscribe @$root, (root) => root?.parent = this
deserializeParams: (params) ->
params.root = atom.deserializers.deserialize(params.root)
params
serializeParams: (params) ->
root: @root?.serialize()
replaceChild: (oldChild, newChild) ->
throw new Error("Replacing non-existent child") if oldChild isnt @root
@root = newChild

View File

@@ -1,18 +1,26 @@
Serializable = require 'serializable'
{$, View} = require './space-pen-extensions'
Pane = require './pane'
PaneContainerModel = require './pane-container-model'
# Private: Manages the list of panes within a {WorkspaceView}
module.exports =
class PaneContainer extends View
Serializable.includeInto(this)
atom.deserializers.add(this)
@deserialize: (state) ->
new this(PaneContainerModel.deserialize(state.model))
@content: ->
@div class: 'panes'
initialize: ({root}={}) ->
@setRoot(root)
initialize: (params) ->
if params instanceof PaneContainerModel
@model = params
else
@model = new PaneContainerModel({root: params?.root?.model})
@subscribe @model.$root, 'value', @onRootChanged
@subscribe this, 'pane:attached', (event, pane) =>
@triggerActiveItemChange() if @getActivePane() is pane
@@ -26,15 +34,15 @@ class PaneContainer extends View
@subscribe this, 'pane:active-item-changed', (event, item) =>
@triggerActiveItemChange() if @getActivePaneItem() is item
viewForModel: (model) ->
viewClass = model.getViewClass()
model._view ?= new viewClass(model)
triggerActiveItemChange: ->
@trigger 'pane-container:active-pane-item-changed', [@getActivePaneItem()]
serializeParams: ->
root: @getRoot()?.serialize()
deserializeParams: (params) ->
params.root = atom.deserializers.deserialize(params.root)
params
model: @model.serialize()
### Public ###
@@ -71,11 +79,15 @@ class PaneContainer extends View
getRoot: ->
@children().first().view()
setRoot: (root, {suppressPaneItemChangeEvents}={}) ->
@empty()
setRoot: (root) ->
@model.root = root?.model
onRootChanged: (root) =>
@children().detach()
if root?
@append(root)
root.makeActive?()
view = @viewForModel(root)
@append(view)
view.makeActive?()
removeChild: (child) ->
throw new Error("Removing non-existant child") unless @getRoot() is child

View File

@@ -3,9 +3,11 @@
{Model} = require 'theorist'
Serializable = require 'serializable'
PaneAxisModel = require './pane-axis-model'
Pane = null
module.exports =
class PaneModel extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
@properties
@@ -28,6 +30,8 @@ class PaneModel extends Model
params.activeItem = find params.items, (item) -> item.getUri?() is activeItemUri
params
getViewClass: -> Pane ?= require './pane'
focus: ->
@focused = true

View File

@@ -19,6 +19,9 @@ class Pane extends View
@version: 1
@deserialize: (state) ->
new this(PaneModel.deserialize(state.model))
@content: (wrappedView) ->
@div class: 'pane', tabindex: -1, =>
@div class: 'flexbox-repaint-hack', =>
@@ -37,11 +40,12 @@ class Pane extends View
# Private:
initialize: (args...) ->
if args[0]?.model?
{@model} = args[0]
if args[0] instanceof PaneModel
@model = args[0]
@focusOnAttach = @model.focused
else
@model = new PaneModel(items: args)
@model._view = this
@onItemAdded(item) for item in @items
@viewsByItem = new WeakMap()
@@ -203,52 +207,13 @@ class Pane extends View
viewForActiveItem: ->
@viewForItem(@activeItem)
# Public: Creates a new pane above with a copy of the currently focused item.
splitUp: (items...) ->
@split(items, 'column', 'before')
splitLeft: (items...) -> @model.splitLeft({items})._view
# Public: Creates a new pane below with a copy of the currently focused item.
splitDown: (items...) ->
@split(items, 'column', 'after')
splitRight: (items...) -> @model.splitRight({items})._view
# Public: Creates a new pane left with a copy of the currently focused item.
splitLeft: (items...) ->
@split(items, 'row', 'before')
splitUp: (items...) -> @model.splitUp({items})._view
# Public: Creates a new pane right with a copy of the currently focused item.
splitRight: (items...) ->
@split(items, 'row', 'after')
# Private:
split: (items, axis, side) ->
PaneContainer = require './pane-container'
parent = @parentModel ? @parent().view()
unless parent.hasClass(axis)
axis = @buildPaneAxis(axis)
if parent instanceof PaneContainer
@detach()
axis.addChild(this)
parent.setRoot(axis)
else
parent.insertChildBefore(this, axis)
axis.addChild(this)
parent = axis
newPane = new Pane(items...)
switch side
when 'before' then parent.insertChildBefore(this, newPane)
when 'after' then parent.insertChildAfter(this, newPane)
newPane.makeActive()
newPane.focus()
newPane
# Private:
buildPaneAxis: (axis) ->
switch axis
when 'row' then new PaneRow()
when 'column' then new PaneColumn()
splitDown: (items...) -> @model.splitDown({items})._view
# Private:
getContainer: ->
@@ -257,13 +222,13 @@ class Pane extends View
# Private:
remove: (selector, keepData) ->
return super if keepData
@parent().view().removeChild(this)
# Private:
beforeRemove: ->
if @is(':has(:focus)')
@getContainer().focusNextPane() or atom.workspaceView?.focus()
else if @isActive()
@getContainer().makeNextPaneActive()
@parent().view().removeChild(this) unless keepData
item.destroy?() for item in @getItems()
super