Remove the old root view properly

This commit is contained in:
Nathan Sobo 2014-09-18 13:42:13 -06:00
parent 32f0eb4f76
commit 465d2afd95
6 changed files with 115 additions and 90 deletions

View File

@ -1,5 +1,6 @@
path = require 'path'
temp = require 'temp'
PaneContainer = require '../src/pane-container'
PaneContainerView = require '../src/pane-container-view'
PaneView = require '../src/pane-view'
{$, View, $$} = require 'atom'
@ -18,7 +19,7 @@ describe "PaneContainerView", ->
save: -> @saved = true
isEqual: (other) -> @name is other?.name
container = new PaneContainerView
container = atom.workspace.getView(atom.workspace.paneContainer).__spacePenView
pane1 = container.getRoot()
pane1.activateItem(new TestView('1'))
pane2 = pane1.splitRight(new TestView('2'))
@ -95,7 +96,7 @@ describe "PaneContainerView", ->
describe "serialization", ->
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
newContainer = new PaneContainerView(container.model.testSerialization())
newContainer = atom.workspace.getView(container.model.testSerialization()).__spacePenView
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
expect(newContainer.find('.pane-row > .pane-column > :contains(2)')).toExist()
expect(newContainer.find('.pane-row > .pane-column > :contains(3)')).toExist()
@ -111,14 +112,14 @@ describe "PaneContainerView", ->
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
it "leaves the empty panes intact", ->
newContainer = new PaneContainerView(container.model.testSerialization())
newContainer = atom.workspace.getView(container.model.testSerialization()).__spacePenView
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
expect(newContainer.find('.pane-row > .pane-column > .pane').length).toBe 2
describe "if the 'core.destroyEmptyPanes' config option is true", ->
it "removes empty panes on deserialization", ->
atom.config.set('core.destroyEmptyPanes', true)
newContainer = new PaneContainerView(container.model.testSerialization())
newContainer = atom.workspace.getView(container.model.testSerialization()).__spacePenView
expect(newContainer.find('.pane-row, .pane-column')).not.toExist()
expect(newContainer.find('> :contains(1)')).toExist()
@ -131,7 +132,7 @@ describe "PaneContainerView", ->
item2b = new TestView('2b')
item3a = new TestView('3a')
container = new PaneContainerView
container = atom.workspace.getView(new PaneContainer).__spacePenView
pane1 = container.getRoot()
pane1.activateItem(item1a)
container.attachToDom()
@ -281,7 +282,7 @@ describe "PaneContainerView", ->
# |7|8|9|
# -------
container = new PaneContainerView
container = atom.workspace.getView(new PaneContainer).__spacePenView
pane1 = container.getRoot()
pane1.activateItem(new TestView('1'))
pane4 = pane1.splitDown(new TestView('4'))

View File

@ -1,4 +1,4 @@
PaneContainerView = require '../src/pane-container-view'
PaneContainer = require '../src/pane-container'
PaneView = require '../src/pane-view'
fs = require 'fs-plus'
{Emitter} = require 'event-kit'
@ -24,7 +24,7 @@ describe "PaneView", ->
beforeEach ->
atom.deserializers.add(TestView)
container = new PaneContainerView
container = atom.workspace.getView(new PaneContainer).__spacePenView
containerModel = container.model
view1 = new TestView(id: 'view-1', text: 'View 1')
view2 = new TestView(id: 'view-2', text: 'View 2')
@ -311,13 +311,13 @@ describe "PaneView", ->
container.attachToDom()
pane.focus()
container2 = new PaneContainerView(container.model.testSerialization())
container2 = atom.workspace.getView(container.model.testSerialization()).__spacePenView
pane2 = container2.getRoot()
container2.attachToDom()
expect(pane2).toMatchSelector(':has(:focus)')
$(document.activeElement).blur()
container3 = new PaneContainerView(container.model.testSerialization())
container3 = atom.workspace.getView(container.model.testSerialization()).__spacePenView
pane3 = container3.getRoot()
container3.attachToDom()
expect(pane3).not.toMatchSelector(':has(:focus)')

View File

@ -0,0 +1,79 @@
{CompositeDisposable} = require 'event-kit'
{callAttachHooks} = require './space-pen-extensions'
PaneContainerView = require './pane-container-view'
_ = require 'underscore-plus'
module.exports =
class PaneContainerElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
@classList.add 'panes'
@__spacePenView = new PaneContainerView(this)
setModel: (@model) ->
@subscriptions.add @model.observeRoot(@rootChanged.bind(this))
@__spacePenView.setModel(@model)
rootChanged: (root) ->
focusedElement = document.activeElement if @hasFocus()
@firstChild?.remove()
if root?
view = @model.getView(root)
@appendChild(view)
callAttachHooks(view)
focusedElement?.focus()
hasFocus: ->
this is document.activeElement or @contains(document.activeElement)
focusPaneViewAbove: ->
@nearestPaneInDirection('above')?.focus()
focusPaneViewBelow: ->
@nearestPaneInDirection('below')?.focus()
focusPaneViewOnLeft: ->
@nearestPaneInDirection('left')?.focus()
focusPaneViewOnRight: ->
@nearestPaneInDirection('right')?.focus()
nearestPaneInDirection: (direction) ->
distance = (pointA, pointB) ->
x = pointB.x - pointA.x
y = pointB.y - pointA.y
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
paneView = @model.getView(@model.getActivePane())
box = @boundingBoxForPaneView(paneView)
paneViews = _.toArray(@querySelectorAll('.pane'))
.filter (otherPaneView) =>
otherBox = @boundingBoxForPaneView(otherPaneView)
switch direction
when 'left' then otherBox.right.x <= box.left.x
when 'right' then otherBox.left.x >= box.right.x
when 'above' then otherBox.bottom.y <= box.top.y
when 'below' then otherBox.top.y >= box.bottom.y
.sort (paneViewA, paneViewB) =>
boxA = @boundingBoxForPaneView(paneViewA)
boxB = @boundingBoxForPaneView(paneViewB)
switch direction
when 'left' then distance(box.left, boxA.right) - distance(box.left, boxB.right)
when 'right' then distance(box.right, boxA.left) - distance(box.right, boxB.left)
when 'above' then distance(box.top, boxA.bottom) - distance(box.top, boxB.bottom)
when 'below' then distance(box.bottom, boxA.top) - distance(box.bottom, boxB.top)
paneViews[0]
boundingBoxForPaneView: (paneView) ->
boundingBox = paneView.getBoundingClientRect()
left: {x: boundingBox.left, y: boundingBox.top}
right: {x: boundingBox.right, y: boundingBox.top}
top: {x: boundingBox.left, y: boundingBox.top}
bottom: {x: boundingBox.left, y: boundingBox.bottom}
module.exports = PaneContainerElement = document.registerElement 'atom-pane-container',
prototype: PaneContainerElement.prototype
extends: 'div'

View File

@ -15,43 +15,20 @@ class PaneContainerView extends View
@content: ->
@div class: 'panes'
initialize: (params) ->
constructor: (@element) ->
super
@subscriptions = new CompositeDisposable
if params instanceof PaneContainer
@model = params
else
@model = new PaneContainer({root: params?.root?.model})
@subscriptions.add @model.observeRoot(@onRootChanged)
setModel: (@model) ->
@subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged)
getRoot: ->
view = @model.getView(@model.getRoot())
view.__spacePenView ? view
onRootChanged: (root) =>
focusedElement = document.activeElement if @hasFocus()
if oldRootView = @model.getView(@model.getRoot())
oldRootView.remove()
if root?
view = @model.getView(root)
@append(view)
callAttachHooks(view)
focusedElement?.focus()
else
atom.workspaceView?.focus() if focusedElement?
onActivePaneItemChanged: (activeItem) =>
@trigger 'pane-container:active-pane-item-changed', [activeItem]
removeChild: (child) ->
throw new Error("Removing non-existant child") unless @getRoot() is child
@setRoot(null)
@trigger 'pane:removed', [child] if child instanceof PaneView
confirmClose: ->
saved = true
for paneView in @getPaneViews()
@ -102,56 +79,17 @@ class PaneContainerView extends View
@model.activatePreviousPane()
focusPaneViewAbove: ->
@nearestPaneInDirection('above')?.focus()
@element.focusPaneViewAbove()
focusPaneViewBelow: ->
@nearestPaneInDirection('below')?.focus()
@element.focusPaneViewBelow()
focusPaneViewOnLeft: ->
@nearestPaneInDirection('left')?.focus()
@element.focusPaneViewOnLeft()
focusPaneViewOnRight: ->
@nearestPaneInDirection('right')?.focus()
@element.focusPaneViewOnRight()
nearestPaneInDirection: (direction) ->
distance = (pointA, pointB) ->
x = pointB.x - pointA.x
y = pointB.y - pointA.y
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
paneView = @getActivePaneView()
box = @boundingBoxForPaneView(paneView)
paneViews = @getPaneViews()
.filter (otherPaneView) =>
otherBox = @boundingBoxForPaneView(otherPaneView)
switch direction
when 'left' then otherBox.right.x <= box.left.x
when 'right' then otherBox.left.x >= box.right.x
when 'above' then otherBox.bottom.y <= box.top.y
when 'below' then otherBox.top.y >= box.bottom.y
.sort (paneViewA, paneViewB) =>
boxA = @boundingBoxForPaneView(paneViewA)
boxB = @boundingBoxForPaneView(paneViewB)
switch direction
when 'left' then distance(box.left, boxA.right) - distance(box.left, boxB.right)
when 'right' then distance(box.right, boxA.left) - distance(box.right, boxB.left)
when 'above' then distance(box.top, boxA.bottom) - distance(box.top, boxB.bottom)
when 'below' then distance(box.bottom, boxA.top) - distance(box.bottom, boxB.top)
paneViews[0]
boundingBoxForPaneView: (paneView) ->
boundingBox = paneView[0].getBoundingClientRect()
left: {x: boundingBox.left, y: boundingBox.top}
right: {x: boundingBox.right, y: boundingBox.top}
top: {x: boundingBox.left, y: boundingBox.top}
bottom: {x: boundingBox.left, y: boundingBox.bottom}
# Deprecated
getPanes: ->
deprecate("Use PaneContainerView::getPaneViews() instead")
@getPaneViews()
beforeRemove: ->
@subscriptions.dispose()

View File

@ -4,11 +4,11 @@
Serializable = require 'serializable'
Pane = require './pane'
PaneElement = require './pane-element'
PaneAxis = require './pane-axis'
PaneContainerElement = require './pane-container-element'
PaneAxisElement = require './pane-axis-element'
PaneAxis = require './pane-axis'
ViewRegistry = require './view-registry'
ItemRegistry = require './item-registry'
PaneContainerView = null
module.exports =
class PaneContainer extends Model
@ -35,12 +35,7 @@ class PaneContainer extends Model
@itemRegistry = new ItemRegistry
@viewRegistry = params?.viewRegistry ? new ViewRegistry
@viewRegistry.addViewProvider
modelConstructor: Pane
viewConstructor: PaneElement
@viewRegistry.addViewProvider
modelConstructor: PaneAxis
viewConstructor: PaneAxisElement
@registerViewProviders()
@setRoot(params?.root ? new Pane)
@destroyEmptyPanes() if params?.destroyEmptyPanes
@ -58,8 +53,18 @@ class PaneContainer extends Model
root: @root?.serialize()
activePaneId: @activePane.id
getViewClass: ->
PaneContainerView ?= require './pane-container-view'
registerViewProviders: ->
@viewRegistry.addViewProvider
modelConstructor: PaneContainer
viewConstructor: PaneContainerElement
@viewRegistry.addViewProvider
modelConstructor: PaneAxis
viewConstructor: PaneAxisElement
@viewRegistry.addViewProvider
modelConstructor: Pane
viewConstructor: PaneElement
getView: (object) ->
@viewRegistry.getView(object)

View File

@ -29,11 +29,12 @@ class Workspace extends Model
@delegatesProperty 'activePane', 'activePaneItem', toProperty: 'paneContainer'
@properties
viewRegistry: null
paneContainer: null
fullScreen: false
destroyedItemUris: -> []
constructor: ->
constructor: (params) ->
super
@emitter = new Emitter
@ -61,6 +62,7 @@ class Workspace extends Model
params.viewRegistry = new ViewRegistry
params.paneContainer.viewRegistry = params.viewRegistry
console.log "deserializing pane container"
params.paneContainer = PaneContainer.deserialize(params.paneContainer)
params