mirror of
https://github.com/atom/atom.git
synced 2026-01-22 21:38:10 -05:00
Rename PaneModel to Pane
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
PaneContainerModel = require '../src/pane-container-model'
|
||||
PaneModel = require '../src/pane-model'
|
||||
Pane = require '../src/pane'
|
||||
|
||||
describe "PaneContainerModel", ->
|
||||
describe "serialization", ->
|
||||
@@ -12,7 +12,7 @@ describe "PaneContainerModel", ->
|
||||
@deserialize: -> new this
|
||||
serialize: -> deserializer: 'Item'
|
||||
|
||||
pane1A = new PaneModel(items: [new Item])
|
||||
pane1A = new Pane(items: [new Item])
|
||||
containerA = new PaneContainerModel(root: pane1A)
|
||||
pane2A = pane1A.splitRight(items: [new Item])
|
||||
pane3A = pane2A.splitDown(items: [new Item])
|
||||
@@ -36,7 +36,7 @@ describe "PaneContainerModel", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new PaneModel
|
||||
pane1 = new Pane
|
||||
container = new PaneContainerModel(root: pane1)
|
||||
|
||||
it "references the first pane if no pane has been made active", ->
|
||||
@@ -67,7 +67,7 @@ describe "PaneContainerModel", ->
|
||||
[container, pane, surrenderedFocusHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
pane = new PaneModel
|
||||
pane = new Pane
|
||||
container = new PaneContainerModel(root: pane)
|
||||
container.on 'surrendered-focus', surrenderedFocusHandler = jasmine.createSpy("surrenderedFocusHandler")
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{Model} = require 'theorist'
|
||||
PaneModel = require '../src/pane-model'
|
||||
Pane = require '../src/pane'
|
||||
PaneAxisModel = require '../src/pane-axis-model'
|
||||
PaneContainerModel = require '../src/pane-container-model'
|
||||
|
||||
describe "PaneModel", ->
|
||||
describe "Pane", ->
|
||||
describe "split methods", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new PaneModel(items: ["A"])
|
||||
pane1 = new Pane(items: ["A"])
|
||||
container = new PaneContainerModel(root: pane1)
|
||||
|
||||
describe "::splitLeft(params)", ->
|
||||
@@ -87,14 +87,14 @@ describe "PaneModel", ->
|
||||
describe "::destroyItem(item)", ->
|
||||
describe "when the last item is destroyed", ->
|
||||
it "destroys the pane", ->
|
||||
pane = new PaneModel(items: ["A", "B"])
|
||||
pane = new Pane(items: ["A", "B"])
|
||||
pane.destroyItem("A")
|
||||
pane.destroyItem("B")
|
||||
expect(pane.isDestroyed()).toBe true
|
||||
|
||||
describe "when an item emits a destroyed event", ->
|
||||
it "removes it from the list of items", ->
|
||||
pane = new PaneModel(items: [new Model, new Model, new Model])
|
||||
pane = new Pane(items: [new Model, new Model, new Model])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.items[1].destroy()
|
||||
expect(pane.items).toEqual [item1, item3]
|
||||
@@ -103,7 +103,7 @@ describe "PaneModel", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new PaneModel(items: [new Model, new Model])
|
||||
pane1 = new Pane(items: [new Model, new Model])
|
||||
container = new PaneContainerModel(root: pane1)
|
||||
|
||||
it "destroys the pane's destroyable items", ->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{Model} = require 'theorist'
|
||||
Serializable = require 'serializable'
|
||||
PaneModel = require './pane-model'
|
||||
Pane = require './pane'
|
||||
|
||||
module.exports =
|
||||
class PaneContainerModel extends Model
|
||||
@@ -56,7 +56,7 @@ class PaneContainerModel extends Model
|
||||
root.parent = this
|
||||
root.container = this
|
||||
|
||||
if root instanceof PaneModel
|
||||
if root instanceof Pane
|
||||
@activePane ?= root
|
||||
@subscribe root, 'destroyed', =>
|
||||
@activePane = null
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
|
||||
PaneModel = require './pane-model'
|
||||
Pane = require './pane'
|
||||
|
||||
# Public: A container which can contains multiple items to be switched between.
|
||||
#
|
||||
@@ -18,7 +18,7 @@ class PaneView extends View
|
||||
@version: 1
|
||||
|
||||
@deserialize: (state) ->
|
||||
new this(PaneModel.deserialize(state.model))
|
||||
new this(Pane.deserialize(state.model))
|
||||
|
||||
@content: (wrappedView) ->
|
||||
@div class: 'pane', tabindex: -1, =>
|
||||
@@ -36,10 +36,10 @@ class PaneView extends View
|
||||
|
||||
# Private:
|
||||
initialize: (args...) ->
|
||||
if args[0] instanceof PaneModel
|
||||
if args[0] instanceof Pane
|
||||
@model = args[0]
|
||||
else
|
||||
@model = new PaneModel(items: args)
|
||||
@model = new Pane(items: args)
|
||||
@model._view = this
|
||||
|
||||
@onItemAdded(item) for item in @items
|
||||
@@ -86,7 +86,7 @@ class PaneView extends View
|
||||
@command 'pane:close-other-items', => @destroyInactiveItems()
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.model = PaneModel.deserialize(params.model)
|
||||
params.model = Pane.deserialize(params.model)
|
||||
params
|
||||
|
||||
serializeParams: ->
|
||||
|
||||
307
src/pane.coffee
Normal file
307
src/pane.coffee
Normal file
@@ -0,0 +1,307 @@
|
||||
{find, compact, extend} = require 'underscore-plus'
|
||||
{dirname} = require 'path'
|
||||
{Model, Sequence} = require 'theorist'
|
||||
Serializable = require 'serializable'
|
||||
PaneAxisModel = require './pane-axis-model'
|
||||
PaneView = null
|
||||
|
||||
# Public: A container for multiple items, one of which is *active* at a given
|
||||
# time. With the default packages, a tab is displayed for each item and the
|
||||
# active item's view is displayed.
|
||||
module.exports =
|
||||
class Pane extends Model
|
||||
atom.deserializers.add(this)
|
||||
Serializable.includeInto(this)
|
||||
|
||||
@properties
|
||||
container: null
|
||||
activeItem: null
|
||||
focused: false
|
||||
|
||||
# Public: Only one pane is considered *active* at a time. A pane is activated
|
||||
# when it is focused, and when focus returns to the pane container after
|
||||
# moving to another element such as a panel, it returns to the active pane.
|
||||
@behavior 'active', ->
|
||||
@$container
|
||||
.switch((container) -> container?.$activePane)
|
||||
.map((activePane) => activePane is this)
|
||||
.distinctUntilChanged()
|
||||
|
||||
# Private:
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
||||
@items = Sequence.fromArray(params?.items ? [])
|
||||
@activeItem ?= @items[0]
|
||||
|
||||
@subscribe @items.onEach (item) =>
|
||||
if typeof item.on is 'function'
|
||||
@subscribe item, 'destroyed', => @removeItem(item)
|
||||
|
||||
@subscribe @items.onRemoval (item, index) =>
|
||||
@unsubscribe item if typeof item.on is 'function'
|
||||
|
||||
@activate() if params?.active
|
||||
|
||||
# Private: Called by the Serializable mixin during serialization.
|
||||
serializeParams: ->
|
||||
items: compact(@items.map((item) -> item.serialize?()))
|
||||
activeItemUri: @activeItem?.getUri?()
|
||||
focused: @focused
|
||||
active: @active
|
||||
|
||||
# Private: Called by the Serializable mixin during deserialization.
|
||||
deserializeParams: (params) ->
|
||||
{items, activeItemUri} = params
|
||||
params.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState))
|
||||
params.activeItem = find params.items, (item) -> item.getUri?() is activeItemUri
|
||||
params
|
||||
|
||||
# Private: Called by the view layer to construct a view for this model.
|
||||
getViewClass: -> PaneView ?= require './pane-view'
|
||||
|
||||
isActive: -> @active
|
||||
|
||||
# Private: Called by the view layer to indicate that the pane has gained focus.
|
||||
focus: ->
|
||||
@focused = true
|
||||
@activate() unless @isActive()
|
||||
|
||||
# Private: Called by the view layer to indicate that the pane has lost focus.
|
||||
blur: ->
|
||||
@focused = false
|
||||
true # if this is called from an event handler, don't cancel it
|
||||
|
||||
# Public: Makes this pane the *active* pane, causing it to gain focus
|
||||
# immediately.
|
||||
activate: ->
|
||||
@container?.activePane = this
|
||||
@emit 'activated'
|
||||
|
||||
# Private:
|
||||
getPanes: -> [this]
|
||||
|
||||
# Public:
|
||||
getItems: ->
|
||||
@items.slice()
|
||||
|
||||
# Public: Returns the item at the specified index.
|
||||
itemAtIndex: (index) ->
|
||||
@items[index]
|
||||
|
||||
# Public: Makes the next item active.
|
||||
activateNextItem: ->
|
||||
index = @getActiveItemIndex()
|
||||
if index < @items.length - 1
|
||||
@activateItemAtIndex(index + 1)
|
||||
else
|
||||
@activateItemAtIndex(0)
|
||||
|
||||
# Public: Makes the previous item active.
|
||||
activatePreviousItem: ->
|
||||
index = @getActiveItemIndex()
|
||||
if index > 0
|
||||
@activateItemAtIndex(index - 1)
|
||||
else
|
||||
@activateItemAtIndex(@items.length - 1)
|
||||
|
||||
# Public: Returns the index of the current active item.
|
||||
getActiveItemIndex: ->
|
||||
@items.indexOf(@activeItem)
|
||||
|
||||
# Public: Makes the item at the given index active.
|
||||
activateItemAtIndex: (index) ->
|
||||
@activateItem(@itemAtIndex(index))
|
||||
|
||||
# Public: Makes the given item active, adding the item if necessary.
|
||||
activateItem: (item) ->
|
||||
if item?
|
||||
@addItem(item)
|
||||
@activeItem = item
|
||||
|
||||
# Public: Adds the item to the pane.
|
||||
#
|
||||
# * item:
|
||||
# The item to add. It can be a model with an associated view or a view.
|
||||
# * index:
|
||||
# An optional index at which to add the item. If omitted, the item is
|
||||
# added to the end.
|
||||
#
|
||||
# Returns the added item
|
||||
addItem: (item, index=@getActiveItemIndex() + 1) ->
|
||||
return if item in @items
|
||||
|
||||
@items.splice(index, 0, item)
|
||||
@emit 'item-added', item, index
|
||||
item
|
||||
|
||||
# Private:
|
||||
removeItem: (item, destroying) ->
|
||||
index = @items.indexOf(item)
|
||||
return if index is -1
|
||||
@activateNextItem() if item is @activeItem and @items.length > 1
|
||||
@items.splice(index, 1)
|
||||
@emit 'item-removed', item, index, destroying
|
||||
@destroy() if @items.length is 0
|
||||
|
||||
# Public: Moves the given item to the specified index.
|
||||
moveItem: (item, newIndex) ->
|
||||
oldIndex = @items.indexOf(item)
|
||||
@items.splice(oldIndex, 1)
|
||||
@items.splice(newIndex, 0, item)
|
||||
@emit 'item-moved', item, newIndex
|
||||
|
||||
# Public: Moves the given item to the given index at another pane.
|
||||
moveItemToPane: (item, pane, index) ->
|
||||
pane.addItem(item, index)
|
||||
@removeItem(item)
|
||||
|
||||
# Public: Destroys the currently active item and make the next item active.
|
||||
destroyActiveItem: ->
|
||||
@destroyItem(@activeItem)
|
||||
false
|
||||
|
||||
# Public: Destroys the given item. If it is the active item, activate the next
|
||||
# one. If this is the last item, also destroys the pane.
|
||||
destroyItem: (item) ->
|
||||
@emit 'before-item-destroyed', item
|
||||
if @promptToSaveItem(item)
|
||||
@emit 'item-destroyed', item
|
||||
@removeItem(item, true)
|
||||
item.destroy?()
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
# Public: Destroys all items and destroys the pane.
|
||||
destroyItems: ->
|
||||
@destroyItem(item) for item in @getItems()
|
||||
|
||||
# Public: Destroys all items but the active one.
|
||||
destroyInactiveItems: ->
|
||||
@destroyItem(item) for item in @getItems() when item isnt @activeItem
|
||||
|
||||
# Private: Called by model superclass.
|
||||
destroyed: ->
|
||||
@container.activateNextPane() if @isActive()
|
||||
item.destroy?() for item in @items.slice()
|
||||
|
||||
# Public: Prompts the user to save the given item if it can be saved and is
|
||||
# currently unsaved.
|
||||
promptToSaveItem: (item) ->
|
||||
return true unless item.shouldPromptToSave?()
|
||||
|
||||
uri = item.getUri()
|
||||
chosen = atom.confirm
|
||||
message: "'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
|
||||
detailedMessage: "Your changes will be lost if you close this item without saving."
|
||||
buttons: ["Save", "Cancel", "Don't Save"]
|
||||
|
||||
switch chosen
|
||||
when 0 then @saveItem(item, -> true)
|
||||
when 1 then false
|
||||
when 2 then true
|
||||
|
||||
# Public: Saves the active item.
|
||||
saveActiveItem: ->
|
||||
@saveItem(@activeItem)
|
||||
|
||||
# Public: Saves the active item at a prompted-for location.
|
||||
saveActiveItemAs: ->
|
||||
@saveItemAs(@activeItem)
|
||||
|
||||
# Public: Saves the specified item.
|
||||
#
|
||||
# * item: The item to save.
|
||||
# * nextAction: An optional function which will be called after the item is saved.
|
||||
saveItem: (item, nextAction) ->
|
||||
if item.getUri?()
|
||||
item.save?()
|
||||
nextAction?()
|
||||
else
|
||||
@saveItemAs(item, nextAction)
|
||||
|
||||
# Public: Saves the given item at a prompted-for location.
|
||||
#
|
||||
# * item: The item to save.
|
||||
# * nextAction: An optional function which will be called after the item is saved.
|
||||
saveItemAs: (item, nextAction) ->
|
||||
return unless item.saveAs?
|
||||
|
||||
itemPath = item.getPath?()
|
||||
itemPath = dirname(itemPath) if itemPath
|
||||
path = atom.showSaveDialogSync(itemPath)
|
||||
if path
|
||||
item.saveAs(path)
|
||||
nextAction?()
|
||||
|
||||
# Public: Saves all items.
|
||||
saveItems: ->
|
||||
@saveItem(item) for item in @getItems()
|
||||
|
||||
# Public: Returns the first item that matches the given URI or undefined if
|
||||
# none exists.
|
||||
itemForUri: (uri) ->
|
||||
find @items, (item) -> item.getUri?() is uri
|
||||
|
||||
# Public: Activates the first item that matches the given URI. Returns a
|
||||
# boolean indicating whether a matching item was found.
|
||||
activateItemForUri: (uri) ->
|
||||
if item = @itemForUri(uri)
|
||||
@activateItem(item)
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
# Private:
|
||||
copyActiveItem: ->
|
||||
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
|
||||
|
||||
# Public: Creates a new pane to the left of the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitLeft: (params) ->
|
||||
@split('horizontal', 'before', params)
|
||||
|
||||
# Public: Creates a new pane to the right of the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitRight: (params) ->
|
||||
@split('horizontal', 'after', params)
|
||||
|
||||
# Public: Creates a new pane above the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitUp: (params) ->
|
||||
@split('vertical', 'before', params)
|
||||
|
||||
# Public: Creates a new pane below the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitDown: (params) ->
|
||||
@split('vertical', 'after', params)
|
||||
|
||||
# Private:
|
||||
split: (orientation, side, params) ->
|
||||
if @parent.orientation isnt orientation
|
||||
@parent.replaceChild(this, new PaneAxisModel({@container, orientation, children: [this]}))
|
||||
|
||||
newPane = new @constructor(extend({focused: true}, params))
|
||||
switch side
|
||||
when 'before' then @parent.insertChildBefore(this, newPane)
|
||||
when 'after' then @parent.insertChildAfter(this, newPane)
|
||||
|
||||
newPane.activate()
|
||||
newPane
|
||||
Reference in New Issue
Block a user