Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Commit

Permalink
Merge pull request #424 from atom/fb-mdt-allowed-locations
Browse files Browse the repository at this point in the history
Allowed Locations & Permanent Dock Items
  • Loading branch information
Max Brunsfeld authored Mar 30, 2017
2 parents f9c618d + d657d17 commit b959a04
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 28 deletions.
17 changes: 13 additions & 4 deletions lib/layout.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ module.exports =
@lastCoords = e
pane = @getPaneAt e
itemView = @getItemViewAt e
if pane? and itemView?
item = e.target.item
if pane? and itemView? and item and itemIsAllowedInPane(item, pane)
coords = if not (@isOnlyTabInPane(pane, e.target) or pane.getItems().length is 0)
[e.clientX, e.clientY]
@lastSplit = @updateView itemView, coords
Expand All @@ -25,16 +26,17 @@ module.exports =
return unless @lastCoords? and @getItemViewAt @lastCoords
target = @getPaneAt @lastCoords
return unless target?
tab = e.target
fromPane = tab.pane
item = tab.item
return unless itemIsAllowedInPane(item, toPane ? target)
toPane = switch @lastSplit
when 'left' then target.splitLeft()
when 'right' then target.splitRight()
when 'up' then target.splitUp()
when 'down' then target.splitDown()
tab = e.target
toPane ?= target
fromPane = tab.pane
return if toPane is fromPane
item = tab.item
fromPane.moveItemToPane item, toPane
toPane.activateItem item
toPane.activate()
Expand Down Expand Up @@ -90,3 +92,10 @@ module.exports =

disableView: ->
@view.classList.remove 'visible'

itemIsAllowedInPane = (item, pane) ->
allowedLocations = item.getAllowedLocations?()
return true unless allowedLocations?
container = pane.getContainer()
location = container.getLocation?() ? 'center'
return location in allowedLocations
33 changes: 22 additions & 11 deletions lib/main.coffee
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{Disposable} = require 'atom'
{CompositeDisposable, Disposable} = require 'atom'
FileIcons = require './file-icons'
layout = require './layout'
TabBarView = require './tab-bar-view'
Expand Down Expand Up @@ -48,18 +48,29 @@ module.exports =
for tabBarView in @tabBarViews by -1
tabBarView.closeAllTabs()

@paneSubscription = atom.workspace.observePanes (pane) =>
tabBarView = new TabBarView(pane)
mruListView = new MRUListView
mruListView.initialize(pane)
paneContainers =
center: atom.workspace.getCenter?() ? atom.workspace
left: atom.workspace.getLeftDock?()
right: atom.workspace.getRightDock?()
bottom: atom.workspace.getBottomDock?()

paneElement = atom.views.getView(pane)
paneElement.insertBefore(tabBarView.element, paneElement.firstChild)
subscriptions =
for location, container of paneContainers
continue unless container
container.observePanes (pane) =>
tabBarView = new TabBarView(pane, location)
mruListView = new MRUListView
mruListView.initialize(pane)

@tabBarViews.push(tabBarView)
pane.onDidDestroy => _.remove(@tabBarViews, tabBarView)
@mruListViews.push(mruListView)
pane.onDidDestroy => _.remove(@mruListViews, mruListView)
paneElement = atom.views.getView(pane)
paneElement.insertBefore(tabBarView.element, paneElement.firstChild)

@tabBarViews.push(tabBarView)
pane.onDidDestroy => _.remove(@tabBarViews, tabBarView)
@mruListViews.push(mruListView)
pane.onDidDestroy => _.remove(@mruListViews, mruListView)

@paneSubscription = new CompositeDisposable(subscriptions...)

deactivate: ->
layout.deactivate()
Expand Down
10 changes: 9 additions & 1 deletion lib/tab-bar-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ TabView = require './tab-view'

module.exports =
class TabBarView
constructor: (@pane) ->
constructor: (@pane, @location) ->
@element = document.createElement('ul')
@element.classList.add("list-inline")
@element.classList.add("tab-bar")
Expand Down Expand Up @@ -107,6 +107,7 @@ class TabBarView
didClickCloseIcon: =>
@closeTab(tabView)
return
@location
})
tabView.terminatePendingState() if @isItemMovingBetweenPanes
@tabsByElement.set(tabView.element, tabView)
Expand Down Expand Up @@ -259,6 +260,9 @@ class TabBarView
else if typeof item.getUri is 'function'
itemURI = item.getUri() ? ''

if typeof item.getAllowedLocations is 'function'
event.dataTransfer.setData 'allowed-locations', item.getAllowedLocations().join('|')

if itemURI?
event.dataTransfer.setData 'text/plain', itemURI

Expand Down Expand Up @@ -335,6 +339,8 @@ class TabBarView
fromPaneId = parseInt(event.dataTransfer.getData('from-pane-id'))
fromIndex = parseInt(event.dataTransfer.getData('sortable-index'))
fromPaneIndex = parseInt(event.dataTransfer.getData('from-pane-index'))
allowedLocations = (event.dataTransfer.getData('allowed-locations') or '').trim()
itemIsAllowed = not allowedLocations or allowedLocations.split('|').includes(@location)

hasUnsavedChanges = event.dataTransfer.getData('has-unsaved-changes') is 'true'
modifiedText = event.dataTransfer.getData('modified-text')
Expand All @@ -344,6 +350,8 @@ class TabBarView

@clearDropTarget()

return unless itemIsAllowed

if fromWindowId is @getWindowId()
fromPane = @paneContainer.getPanes()[fromPaneIndex]
if fromPane?.id isnt fromPaneId
Expand Down
11 changes: 6 additions & 5 deletions lib/tab-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ layout = require './layout'

module.exports =
class TabView
constructor: ({@item, @pane, didClickCloseIcon, @tabs}) ->
constructor: ({@item, @pane, didClickCloseIcon, @tabs, location}) ->
if typeof @item.getPath is 'function'
@path = @item.getPath()

Expand All @@ -20,10 +20,11 @@ class TabView
@itemTitle.classList.add('title')
@element.appendChild(@itemTitle)

closeIcon = document.createElement('div')
closeIcon.classList.add('close-icon')
closeIcon.onclick = didClickCloseIcon
@element.appendChild(closeIcon)
if location is 'center' or not @item.isPermanentDockItem?()
closeIcon = document.createElement('div')
closeIcon.classList.add('close-icon')
closeIcon.onclick = didClickCloseIcon
@element.appendChild(closeIcon)

@subscriptions = new CompositeDisposable()

Expand Down
60 changes: 53 additions & 7 deletions spec/tabs-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ describe "TabBarView", ->

class TestView
@deserialize: ({title, longTitle, iconName}) -> new TestView(title, longTitle, iconName)
constructor: (@title, @longTitle, @iconName, @pathURI) ->
constructor: (@title, @longTitle, @iconName, @pathURI, isPermanentDockItem) ->
@_isPermanentDockItem = isPermanentDockItem
@element = document.createElement('div')
@element.textContent = @title
if isPermanentDockItem?
@isPermanentDockItem = -> isPermanentDockItem
getTitle: -> @title
getLongTitle: -> @longTitle
getURI: -> @pathURI
Expand Down Expand Up @@ -98,7 +101,7 @@ describe "TabBarView", ->
addItemToPane(pane, item1, 0)
addItemToPane(pane, item2, 2)
pane.activateItem(item2)
tabBar = new TabBarView(pane)
tabBar = new TabBarView(pane, 'center')

afterEach ->
deserializerDisposable.dispose()
Expand Down Expand Up @@ -377,6 +380,49 @@ describe "TabBarView", ->
expect(tabBar.tabForItem(item1).element.textContent).toMatch "Grumpy Old Man"
expect(tabBar.tabForItem(item2).element.textContent).toMatch "Old Man"

describe "the close button", ->
it "is present in the center, regardless of the value returned by isPermanentDockItem()", ->
item3 = new TestView('Item 3', undefined, "squirrel", "sample.js")
expect(item3.isPermanentDockItem).toBeUndefined()
item4 = new TestView('Item 4', undefined, "squirrel", "sample.js", true)
expect(typeof item4.isPermanentDockItem).toBe('function')
item5 = new TestView('Item 5', undefined, "squirrel", "sample.js", false)
expect(typeof item5.isPermanentDockItem).toBe('function')
pane.activateItem(item3)
pane.activateItem(item4)
pane.activateItem(item5)
tabs = tabBar.element.querySelectorAll('.tab')
expect(tabs[2].querySelector('.close-icon')).not.toEqual(null)
expect(tabs[3].querySelector('.close-icon')).not.toEqual(null)
expect(tabs[4].querySelector('.close-icon')).not.toEqual(null)

return unless atom.workspace.getRightDock?
describe "in docks", ->
beforeEach ->
pane = atom.workspace.getRightDock().getActivePane()
tabBar = new TabBarView(pane, 'right')

it "isn't shown if the method returns true", ->
item1 = new TestView('Item 1', undefined, "squirrel", "sample.js", true)
expect(typeof item1.isPermanentDockItem).toBe('function')
pane.activateItem(item1)
tab = tabBar.element.querySelector('.tab')
expect(tab.querySelector('.close-icon')).toEqual(null)

it "is shown if the method returns false", ->
item1 = new TestView('Item 1', undefined, "squirrel", "sample.js", false)
expect(typeof item1.isPermanentDockItem).toBe('function')
pane.activateItem(item1)
tab = tabBar.element.querySelector('.tab')
expect(tab.querySelector('.close-icon')).not.toBeUndefined()

it "is shown if the method doesn't exist", ->
item1 = new TestView('Item 1', undefined, "squirrel", "sample.js")
expect(item1.isPermanentDockItem).toBeUndefined()
pane.activateItem(item1)
tab = tabBar.element.querySelector('.tab')
expect(tab.querySelector('.close-icon')).not.toEqual(null)

describe "when an item has an icon defined", ->
it "displays the icon on the tab", ->
expect(tabBar.element.querySelectorAll('.tab')[0].querySelector('.title')).toHaveClass "icon"
Expand Down Expand Up @@ -667,7 +713,7 @@ describe "TabBarView", ->
describe "when pane:close is fired", ->
it "destroys all the tabs within the pane", ->
pane2 = pane.splitDown(copyActiveItem: true)
tabBar2 = new TabBarView(pane2)
tabBar2 = new TabBarView(pane2, 'center')
tab2 = tabBar2.tabAtIndex(0)
spyOn(tab2, 'destroy')

Expand Down Expand Up @@ -752,7 +798,7 @@ describe "TabBarView", ->
beforeEach ->
pane2 = pane.splitRight(copyActiveItem: true)
[item2b] = pane2.getItems()
tabBar2 = new TabBarView(pane2)
tabBar2 = new TabBarView(pane2, 'center')

it "removes the tab and item from their original pane and moves them to the target pane", ->
expect(tabBar.getTabs().map (tab) -> tab.element.textContent).toEqual ["Item 1", "sample.js", "Item 2"]
Expand Down Expand Up @@ -994,7 +1040,7 @@ describe "TabBarView", ->
config: atom.config
viewRegistry: atom.views
pane2 = paneContainer.getActivePane()
tabBar2 = new TabBarView(pane2)
tabBar2 = new TabBarView(pane2, 'center')

afterEach ->
workspaceElement.remove()
Expand Down Expand Up @@ -1220,7 +1266,7 @@ describe "TabBarView", ->
it "makes the tab permanent in the new pane", ->
pane.activateItem(editor1)
pane2 = pane.splitRight(copyActiveItem: true)
tabBar2 = new TabBarView(pane2)
tabBar2 = new TabBarView(pane2, 'center')
newEditor = pane2.getActiveItem()
expect(isPending(newEditor)).toBe false
expect(tabBar2.tabForItem(newEditor).element.querySelector('.title')).not.toHaveClass 'temp'
Expand All @@ -1239,7 +1285,7 @@ describe "TabBarView", ->
pane.activateItem(editor1)
pane2 = pane.splitRight()

tabBar2 = new TabBarView(pane2)
tabBar2 = new TabBarView(pane2, 'center')
tabBar2.moveItemBetweenPanes(pane, 0, pane2, 1, editor1)

expect(tabBar2.tabForItem(pane2.getActiveItem()).element.querySelector('.title')).not.toHaveClass 'temp'
Expand Down

0 comments on commit b959a04

Please sign in to comment.