mirror of
https://github.com/atom/atom.git
synced 2026-02-17 01:51:54 -05:00
We don't actually need structural markup to ensure that all pane views are absolutely positioned. We can just use the `> *` selector inside of .pane-items. /cc @probablycorey is there anything I'm missing here?
213 lines
6.4 KiB
CoffeeScript
213 lines
6.4 KiB
CoffeeScript
{$, View} = require './space-pen-extensions'
|
|
Serializable = require 'serializable'
|
|
Delegator = require 'delegato'
|
|
|
|
PaneModel = require './pane-model'
|
|
|
|
# Public: A container which can contains multiple items to be switched between.
|
|
#
|
|
# Items can be almost anything however most commonly they're {EditorView}s.
|
|
#
|
|
# Most packages won't need to use this class, unless you're interested in
|
|
# building a package that deals with switching between panes or tiems.
|
|
module.exports =
|
|
class Pane extends View
|
|
Serializable.includeInto(this)
|
|
Delegator.includeInto(this)
|
|
|
|
@version: 1
|
|
|
|
@deserialize: (state) ->
|
|
new this(PaneModel.deserialize(state.model))
|
|
|
|
@content: (wrappedView) ->
|
|
@div class: 'pane', tabindex: -1, =>
|
|
@div class: 'item-views', outlet: 'itemViews'
|
|
|
|
@delegatesProperties 'items', 'activeItem', toProperty: 'model'
|
|
@delegatesMethods 'getItems', 'showNextItem', 'showPreviousItem', 'getActiveItemIndex',
|
|
'showItemAtIndex', 'showItem', 'addItem', 'itemAtIndex', 'removeItem', 'removeItemAtIndex',
|
|
'moveItem', 'moveItemToPane', 'destroyItem', 'destroyItems', 'destroyActiveItem',
|
|
'destroyInactiveItems', 'saveActiveItem', 'saveActiveItemAs', 'saveItem', 'saveItemAs',
|
|
'saveItems', 'itemForUri', 'showItemForUri', 'promptToSaveItem', 'copyActiveItem',
|
|
'isActive', 'makeActive', toProperty: 'model'
|
|
|
|
previousActiveItem: null
|
|
|
|
# Private:
|
|
initialize: (args...) ->
|
|
if args[0] instanceof PaneModel
|
|
@model = args[0]
|
|
else
|
|
@model = new PaneModel(items: args)
|
|
@model._view = this
|
|
|
|
@onItemAdded(item) for item in @items
|
|
@viewsByItem = new WeakMap()
|
|
@handleEvents()
|
|
|
|
handleEvents: ->
|
|
@subscribe @model, 'destroyed', => @remove()
|
|
|
|
@subscribe @model.$activeItem, @onActiveItemChanged
|
|
@subscribe @model, 'item-added', @onItemAdded
|
|
@subscribe @model, 'item-removed', @onItemRemoved
|
|
@subscribe @model, 'item-moved', @onItemMoved
|
|
@subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed
|
|
@subscribe @model, 'item-destroyed', @onItemDestroyed
|
|
@subscribe @model.$active, @onActiveStatusChanged
|
|
|
|
@subscribe this, 'focusin', => @model.focus()
|
|
@subscribe this, 'focusout', => @model.blur()
|
|
@subscribe this, 'focus', =>
|
|
@activeView?.focus()
|
|
false
|
|
|
|
@command 'pane:save-items', => @saveItems()
|
|
@command 'pane:show-next-item', => @showNextItem()
|
|
@command 'pane:show-previous-item', => @showPreviousItem()
|
|
|
|
@command 'pane:show-item-1', => @showItemAtIndex(0)
|
|
@command 'pane:show-item-2', => @showItemAtIndex(1)
|
|
@command 'pane:show-item-3', => @showItemAtIndex(2)
|
|
@command 'pane:show-item-4', => @showItemAtIndex(3)
|
|
@command 'pane:show-item-5', => @showItemAtIndex(4)
|
|
@command 'pane:show-item-6', => @showItemAtIndex(5)
|
|
@command 'pane:show-item-7', => @showItemAtIndex(6)
|
|
@command 'pane:show-item-8', => @showItemAtIndex(7)
|
|
@command 'pane:show-item-9', => @showItemAtIndex(8)
|
|
|
|
@command 'pane:split-left', => @splitLeft(@copyActiveItem())
|
|
@command 'pane:split-right', => @splitRight(@copyActiveItem())
|
|
@command 'pane:split-up', => @splitUp(@copyActiveItem())
|
|
@command 'pane:split-down', => @splitDown(@copyActiveItem())
|
|
@command 'pane:close', => @destroyItems()
|
|
@command 'pane:close-other-items', => @destroyInactiveItems()
|
|
|
|
deserializeParams: (params) ->
|
|
params.model = PaneModel.deserialize(params.model)
|
|
params
|
|
|
|
serializeParams: ->
|
|
model: @model.serialize()
|
|
|
|
# Private:
|
|
afterAttach: (onDom) ->
|
|
@focus() if @model.focused and onDom
|
|
|
|
return if @attached
|
|
@attached = true
|
|
@trigger 'pane:attached', [this]
|
|
|
|
onActiveStatusChanged: (active) =>
|
|
if active
|
|
@addClass('active')
|
|
@trigger 'pane:became-active'
|
|
else
|
|
@removeClass('active')
|
|
@trigger 'pane:became-inactive'
|
|
|
|
# Public: Returns the next pane, ordered by creation.
|
|
getNextPane: ->
|
|
panes = @getContainer()?.getPanes()
|
|
return unless panes.length > 1
|
|
nextIndex = (panes.indexOf(this) + 1) % panes.length
|
|
panes[nextIndex]
|
|
|
|
getActivePaneItem: ->
|
|
@activeItem
|
|
|
|
onActiveItemChanged: (item) =>
|
|
@previousActiveItem?.off? 'title-changed', @activeItemTitleChanged
|
|
@previousActiveItem = item
|
|
|
|
return unless item?
|
|
|
|
isFocused = @is(':has(:focus)')
|
|
item.on? 'title-changed', @activeItemTitleChanged
|
|
view = @viewForItem(item)
|
|
@itemViews.children().not(view).hide()
|
|
@itemViews.append(view) unless view.parent().is(@itemViews)
|
|
view.show() if @attached
|
|
view.focus() if isFocused
|
|
|
|
@activeView = view
|
|
@trigger 'pane:active-item-changed', [item]
|
|
|
|
onItemAdded: (item, index) =>
|
|
@trigger 'pane:item-added', [item, index]
|
|
|
|
onItemRemoved: (item, index, destroyed) =>
|
|
if item instanceof $
|
|
viewToRemove = item
|
|
else if viewToRemove = @viewsByItem.get(item)
|
|
@viewsByItem.delete(item)
|
|
|
|
removingLastItem = @model.items.length is 0
|
|
hasFocus = @hasFocus()
|
|
|
|
@getContainer().focusNextPane() if hasFocus and removingLastItem
|
|
|
|
if viewToRemove?
|
|
viewToRemove.setModel?(null)
|
|
if destroyed
|
|
viewToRemove.remove()
|
|
else
|
|
viewToRemove.detach()
|
|
|
|
# @focus() if hasFocus and not removingLastItem
|
|
|
|
@trigger 'pane:item-removed', [item, index]
|
|
|
|
onItemMoved: (item, newIndex) =>
|
|
@trigger 'pane:item-moved', [item, newIndex]
|
|
|
|
onBeforeItemDestroyed: (item) =>
|
|
@unsubscribe(item) if typeof item.off is 'function'
|
|
@trigger 'pane:before-item-destroyed', [item]
|
|
|
|
onItemDestroyed: (item) =>
|
|
@getContainer()?.itemDestroyed(item)
|
|
|
|
# Private:
|
|
activeItemTitleChanged: =>
|
|
@trigger 'pane:active-item-title-changed'
|
|
|
|
# Private:
|
|
viewForItem: (item) ->
|
|
if item instanceof $
|
|
item
|
|
else if view = @viewsByItem.get(item)
|
|
view
|
|
else
|
|
viewClass = item.getViewClass()
|
|
view = new viewClass(item)
|
|
@viewsByItem.set(item, view)
|
|
view
|
|
|
|
# Private:
|
|
viewForActiveItem: ->
|
|
@viewForItem(@activeItem)
|
|
|
|
splitLeft: (items...) -> @model.splitLeft({items})._view
|
|
|
|
splitRight: (items...) -> @model.splitRight({items})._view
|
|
|
|
splitUp: (items...) -> @model.splitUp({items})._view
|
|
|
|
splitDown: (items...) -> @model.splitDown({items})._view
|
|
|
|
# Private:
|
|
getContainer: ->
|
|
@closest('.panes').view()
|
|
|
|
beforeRemove: ->
|
|
@getContainer()?.focusNextPane() if @hasFocus()
|
|
@model.destroy() unless @model.isDestroyed()
|
|
|
|
# Private:
|
|
remove: (selector, keepData) ->
|
|
return super if keepData
|
|
@unsubscribe()
|
|
super
|