From 8efcb1abfa4a0edece4e1574d25e1c868d79ab82 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 8 Jan 2014 18:49:50 -0700 Subject: [PATCH] Remove pane splitting/unsplitting logic from view There's still some failing specs around focus management, but it's getting closer. --- src/pane-axis-model.coffee | 20 +++++++++++ src/pane-axis.coffee | 64 +++++++++------------------------ src/pane-container-model.coffee | 11 ++++++ src/pane-container.coffee | 36 ++++++++++++------- src/pane-model.coffee | 4 +++ src/pane.coffee | 61 +++++++------------------------ 6 files changed, 88 insertions(+), 108 deletions(-) diff --git a/src/pane-axis-model.coffee b/src/pane-axis-model.coffee index b8edfbbc5..824727815 100644 --- a/src/pane-axis-model.coffee +++ b/src/pane-axis-model.coffee @@ -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) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 61d29cda9..bdee142ad 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -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() diff --git a/src/pane-container-model.coffee b/src/pane-container-model.coffee index f421d9b3b..1aa975cd7 100644 --- a/src/pane-container-model.coffee +++ b/src/pane-container-model.coffee @@ -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 diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 623c1baf5..bc5f0e2f4 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -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 diff --git a/src/pane-model.coffee b/src/pane-model.coffee index d12376eb9..9b42aaf87 100644 --- a/src/pane-model.coffee +++ b/src/pane-model.coffee @@ -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 diff --git a/src/pane.coffee b/src/pane.coffee index b44b59680..540cbf9f9 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -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