mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
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:
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user