From 465d2afd95072adfc624453eaef3349d6b77ba70 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 18 Sep 2014 13:42:13 -0600 Subject: [PATCH] Remove the old root view properly --- spec/pane-container-view-spec.coffee | 13 ++--- spec/pane-view-spec.coffee | 8 +-- src/pane-container-element.coffee | 79 ++++++++++++++++++++++++++++ src/pane-container-view.coffee | 76 +++----------------------- src/pane-container.coffee | 25 +++++---- src/workspace.coffee | 4 +- 6 files changed, 115 insertions(+), 90 deletions(-) create mode 100644 src/pane-container-element.coffee diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 8bbb75243..2c2a359dc 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -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')) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 2a92ff3d5..3550950c7 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -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)') diff --git a/src/pane-container-element.coffee b/src/pane-container-element.coffee new file mode 100644 index 000000000..c76a81508 --- /dev/null +++ b/src/pane-container-element.coffee @@ -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' diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 72d24d1dd..6f59f6fd2 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -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() diff --git a/src/pane-container.coffee b/src/pane-container.coffee index b60ac3c30..07c86c8a8 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -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) diff --git a/src/workspace.coffee b/src/workspace.coffee index 35f8577a2..126ac89a6 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -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