Merge pull request #3837 from atom/bo-panel-api

Add panel API
This commit is contained in:
Ben Ogle
2014-10-20 13:25:48 -07:00
15 changed files with 618 additions and 33 deletions

View File

@@ -0,0 +1,30 @@
{CompositeDisposable} = require 'event-kit'
class PanelContainerElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
getModel: -> @model
setModel: (@model) ->
@subscriptions.add @model.onDidAddPanel(@panelAdded.bind(this))
@subscriptions.add @model.onDidRemovePanel(@panelRemoved.bind(this))
@subscriptions.add @model.onDidDestroy(@destroyed.bind(this))
@setAttribute('location', @model.getLocation())
panelAdded: ({panel, index}) ->
if index >= @childNodes.length
@appendChild(panel.getView())
else
referenceItem = @childNodes[index + 1]
@insertBefore(panel.getView(), referenceItem)
panelRemoved: ({panel, index}) ->
@removeChild(@childNodes[index])
destroyed: ->
@subscriptions.dispose()
@parentNode?.removeChild(this)
module.exports = PanelContainerElement = document.registerElement 'atom-panel-container', prototype: PanelContainerElement.prototype

View File

@@ -0,0 +1,66 @@
{Emitter, CompositeDisposable} = require 'event-kit'
module.exports =
class PanelContainer
constructor: ({@viewRegistry, @location}) ->
@emitter = new Emitter
@subscriptions = new CompositeDisposable
@panels = []
destroy: ->
pane.destroy() for pane in @getPanels()
@subscriptions.dispose()
@emitter.emit 'did-destroy', this
@emitter.dispose()
###
Section: Event Subscription
###
onDidAddPanel: (callback) ->
@emitter.on 'did-add-panel', callback
onDidRemovePanel: (callback) ->
@emitter.on 'did-remove-panel', callback
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
###
Section: Panels
###
getView: -> @viewRegistry.getView(this)
getLocation: -> @location
getPanels: -> @panels
addPanel: (panel) ->
@subscriptions.add panel.onDidDestroy(@panelDestoryed.bind(this))
index = @getPanelIndex(panel)
if index is @panels.length
@panels.push(panel)
else
@panels.splice(index, 0, panel)
@emitter.emit 'did-add-panel', {panel, index}
panel
panelDestoryed: (panel) ->
index = @panels.indexOf(panel)
if index > -1
@panels.splice(index, 1)
@emitter.emit 'did-remove-panel', {panel, index}
getPanelIndex: (panel) ->
priority = panel.getPriority()
if @location in ['bottom', 'right']
for p, i in @panels by -1
return i + 1 if priority < p.getPriority()
0
else
for p, i in @panels
return i if priority < p.getPriority()
@panels.length

31
src/panel-element.coffee Normal file
View File

@@ -0,0 +1,31 @@
{CompositeDisposable} = require 'event-kit'
{callAttachHooks} = require './space-pen-extensions'
class PanelElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
getModel: -> @model
setModel: (@model) ->
view = @model.getItemView()
@appendChild(view)
callAttachHooks(view) # for backward compatibility with SpacePen views
@subscriptions.add @model.onDidChangeVisible(@visibleChanged.bind(this))
@subscriptions.add @model.onDidDestroy(@destroyed.bind(this))
attachedCallback: ->
@visibleChanged(@model.isVisible())
visibleChanged: (visible) ->
if visible
@style.display = null
else
@style.display = 'none'
destroyed: ->
@subscriptions.dispose()
@parentNode?.removeChild(this)
module.exports = PanelElement = document.registerElement 'atom-panel', prototype: PanelElement.prototype

78
src/panel.coffee Normal file
View File

@@ -0,0 +1,78 @@
{Emitter} = require 'event-kit'
# Extended: A container representing a panel on the edges of the editor window.
# You should not create a `Panel` directly, instead use {Workspace::addTopPanel}
# and friends to add panels.
#
# Examples: [tree-view](https://github.com/atom/tree-view),
# [status-bar](https://github.com/atom/status-bar),
# and [find-and-replace](https://github.com/atom/find-and-replace) all use
# panels.
module.exports =
class Panel
###
Section: Construction and Destruction
###
constructor: ({@viewRegistry, @item, @visible, @priority}) ->
@emitter = new Emitter
@visible ?= true
@priority ?= 100
# Public: Destroy and remove this panel from the UI.
destroy: ->
@emitter.emit 'did-destroy', this
###
Section: Event Subscription
###
# Public: Invoke the given callback when the pane hidden or shown.
#
# * `callback` {Function} to be called when the pane is destroyed.
# * `visible` {Boolean} true when the panel has been shown
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidChangeVisible: (callback) ->
@emitter.on 'did-change-visible', callback
# Public: Invoke the given callback when the pane is destroyed.
#
# * `callback` {Function} to be called when the pane is destroyed.
# * `panel` {Panel} this panel
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
###
Section: Panel Details
###
# Public: Gets this panel model's view DOM node.
#
# Returns an `<atom-panel>` {Element}
getView: -> @viewRegistry.getView(this)
# Public: Gets your panel contents view.
#
# Returns an {Element} or jQuery element, depeneding on how you created the panel.
getItemView: -> @viewRegistry.getView(@item)
# Public: Returns a {Number} indicating this panel's priority.
getPriority: -> @priority
# Public: Returns a {Boolean} true when the panel is visible.
isVisible: -> @visible
# Public: Hide this panel
hide: ->
wasVisible = @visible
@visible = false
@emitter.emit 'did-change-visible', @visible if wasVisible
# Public: Show this panel
show: ->
wasVisible = @visible
@visible = true
@emitter.emit 'did-change-visible', @visible unless wasVisible

View File

@@ -72,6 +72,18 @@ class WorkspaceElement extends HTMLElement
window.addEventListener 'focus', handleWindowFocus
@subscriptions.add(new Disposable -> window.removeEventListener 'focus', handleWindowFocus)
@panelContainers =
top: @model.panelContainers.top.getView()
left: @model.panelContainers.left.getView()
right: @model.panelContainers.right.getView()
bottom: @model.panelContainers.bottom.getView()
@horizontalAxis.insertBefore(@panelContainers.left, @verticalAxis)
@horizontalAxis.appendChild(@panelContainers.right)
@verticalAxis.insertBefore(@panelContainers.top, @paneContainer)
@verticalAxis.appendChild(@panelContainers.bottom)
@__spacePenView.setModel(@model)
setTextEditorFontSize: (fontSize) ->

View File

@@ -141,59 +141,36 @@ class WorkspaceView extends View
Section: Adding elements to the workspace
###
# Essential: Prepend an element or view to the panels at the top of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToTop: (element) ->
deprecate 'Please use Workspace::addTopPanel() instead'
@vertical.prepend(element)
# Essential: Append an element or view to the panels at the top of the workspace.
#
# * `element` jQuery object or DOM element
appendToTop: (element) ->
deprecate 'Please use Workspace::addTopPanel() instead'
@panes.before(element)
# Essential: Prepend an element or view to the panels at the bottom of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToBottom: (element) ->
deprecate 'Please use Workspace::addBottomPanel() instead'
@panes.after(element)
# Essential: Append an element or view to the panels at the bottom of the
# workspace.
#
# * `element` jQuery object or DOM element
appendToBottom: (element) ->
deprecate 'Please use Workspace::addBottomPanel() instead'
@vertical.append(element)
# Essential: Prepend an element or view to the panels at the left of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToLeft: (element) ->
deprecate 'Please use Workspace::addLeftPanel() instead'
@horizontal.prepend(element)
# Essential: Append an element or view to the panels at the left of the
# workspace.
#
# * `element` jQuery object or DOM element
appendToLeft: (element) ->
deprecate 'Please use Workspace::addLeftPanel() instead'
@vertical.before(element)
# Essential: Prepend an element or view to the panels at the right of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToRight: (element) ->
deprecate 'Please use Workspace::addRightPanel() instead'
@vertical.after(element)
# Essential: Append an element or view to the panels at the right of the
# workspace.
#
# * `element` jQuery object or DOM element
appendToRight: (element) ->
deprecate 'Please use Workspace::addRightPanel() instead'
@horizontal.append(element)
###

View File

@@ -10,6 +10,10 @@ Grim = require 'grim'
TextEditor = require './text-editor'
PaneContainer = require './pane-container'
Pane = require './pane'
Panel = require './panel'
PanelElement = require './panel-element'
PanelContainer = require './panel-container'
PanelContainerElement = require './panel-container-element'
ViewRegistry = require './view-registry'
WorkspaceElement = require './workspace-element'
@@ -45,6 +49,12 @@ class Workspace extends Model
@paneContainer ?= new PaneContainer({@viewRegistry})
@paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed)
@panelContainers =
top: new PanelContainer({@viewRegistry, location: 'top'})
left: new PanelContainer({@viewRegistry, location: 'left'})
right: new PanelContainer({@viewRegistry, location: 'right'})
bottom: new PanelContainer({@viewRegistry, location: 'bottom'})
@subscribeToActiveItem()
@addOpener (filePath) =>
@@ -62,6 +72,14 @@ class Workspace extends Model
modelConstructor: Workspace
viewConstructor: WorkspaceElement
@addViewProvider
modelConstructor: PanelContainer
viewConstructor: PanelContainerElement
@addViewProvider
modelConstructor: Panel
viewConstructor: PanelElement
# Called by the Serializable mixin during deserialization
deserializeParams: (params) ->
for packageName in params.packagesWithActiveGrammars ? []
@@ -578,6 +596,76 @@ class Workspace extends Model
@paneContainer.destroy()
@activeItemSubscriptions?.dispose()
###
Section: Panels
###
# Essential: Adds a panel item to the bottom of the editor window.
#
# * `options` {Object}
# * `item` Your panel content. It can be DOM element, a jQuery element, or
# a model with a view registered via {::addViewProvider}. We recommend the
# latter. See {::addViewProvider} for more information.
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
# (default: true)
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
# forced closer to the edges of the window. (default: 100)
#
# Returns a {Panel}
addBottomPanel: (options) ->
@addPanel('bottom', options)
# Essential: Adds a panel item to the left of the editor window.
#
# * `options` {Object}
# * `item` Your panel content. It can be DOM element, a jQuery element, or
# a model with a view registered via {::addViewProvider}. We recommend the
# latter. See {::addViewProvider} for more information.
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
# (default: true)
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
# forced closer to the edges of the window. (default: 100)
#
# Returns a {Panel}
addLeftPanel: (options) ->
@addPanel('left', options)
# Essential: Adds a panel item to the right of the editor window.
#
# * `options` {Object}
# * `item` Your panel content. It can be DOM element, a jQuery element, or
# a model with a view registered via {::addViewProvider}. We recommend the
# latter. See {::addViewProvider} for more information.
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
# (default: true)
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
# forced closer to the edges of the window. (default: 100)
#
# Returns a {Panel}
addRightPanel: (options) ->
@addPanel('right', options)
# Essential: Adds a panel item to the top of the editor window above the tabs.
#
# * `options` {Object}
# * `item` Your panel content. It can be DOM element, a jQuery element, or
# a model with a view registered via {::addViewProvider}. We recommend the
# latter. See {::addViewProvider} for more information.
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
# (default: true)
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
# forced closer to the edges of the window. (default: 100)
#
# Returns a {Panel}
addTopPanel: (options) ->
@addPanel('top', options)
addPanel: (location, options) ->
options ?= {}
options.viewRegistry = @viewRegistry
@panelContainers[location].addPanel(new Panel(options))
###
Section: View Management
###
@@ -628,7 +716,7 @@ class Workspace extends Model
# makes [HTML 5 custom elements](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/)
# an ideal tool for implementing views in Atom.
#
# ## Example
# ## Examples
#
# Text editors are divided into a model and a view layer, so when you interact
# with methods like `atom.workspace.getActiveTextEditor()` you're only going