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

Drag multiple items in tree-view #793

Closed
wants to merge 10 commits into from
54 changes: 34 additions & 20 deletions lib/tree-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -738,12 +738,17 @@ class TreeView extends View
onMouseDown: (e) ->
e.stopPropagation()

# return early if we're opening a contextual menu (right click) during multi-select mode
if @multiSelectEnabled() and
e.currentTarget.classList.contains('selected') and
# mouse right click or ctrl click as right click on darwin platforms
(e.button is 2 or e.ctrlKey and process.platform is 'darwin')
return
if @multiSelectEnabled() and e.currentTarget.classList.contains('selected')
# return early if we're opening a contextual menu (right click) during multi-select mode
if (e.button is 2 or e.ctrlKey and process.platform is 'darwin')
return
# return early unless we're deselecting an entry (metaKey on OSX, ctrl/metaKey on linux/windows)
if process.platform is 'darwin'
if not e.metaKey
return
else if process.platform isnt 'darwin'
if not (e.ctrlKey or e.metaKey)
return

entryToSelect = e.currentTarget

Expand Down Expand Up @@ -835,26 +840,33 @@ class TreeView extends View
onDragStart: (e) ->
e.stopPropagation()

target = $(e.currentTarget).find(".name")
initialPath = target.data("path")
initialPaths = []
targets = []

style = getStyleObject(target[0])

fileNameElement = target.clone()
.css(style)
eventTarget = $(e.currentTarget).find('.name')
dragImage = $('<ol></ol>', {class: 'entries list-tree'})
.css(
position: 'absolute'
top: 0
left: 0
)
fileNameElement.appendTo(document.body)

e.originalEvent.dataTransfer.effectAllowed = "move"
e.originalEvent.dataTransfer.setDragImage(fileNameElement[0], 0, 0)
e.originalEvent.dataTransfer.setData("initialPath", initialPath)
for entry in @getSelectedEntries()
entryPath = $(entry).find('.name').data('path')
unless path.dirname(entryPath) in initialPaths
initialPaths.push(entryPath)
image = $(entry).clone()
image.find('.entry').addBack('.entry').removeClass('selected')
dragImage.append(image)

dragImage.appendTo(document.body)

e.originalEvent.dataTransfer.effectAllowed = 'move'
e.originalEvent.dataTransfer.setDragImage(dragImage[0], 0, 0)
e.originalEvent.dataTransfer.setData('initialPaths', initialPaths)

window.requestAnimationFrame ->
fileNameElement.remove()
dragImage.remove()

# Handle entry dragover event; reset default dragover actions
onDragOver: (e) ->
Expand All @@ -874,11 +886,13 @@ class TreeView extends View
newDirectoryPath = $(entry).find(".name").data("path")
return false unless newDirectoryPath

initialPath = e.originalEvent.dataTransfer.getData("initialPath")
initialPaths = e.originalEvent.dataTransfer.getData("initialPaths")

if initialPath
if initialPaths
# Drop event from Atom
@moveEntry(initialPath, newDirectoryPath)
# iterate backwards so files in a dir are moved before the dir itself
for initialPath in initialPaths.split(',') by -1
@moveEntry(initialPath, newDirectoryPath)
else
# Drop event from OS
for file in e.originalEvent.dataTransfer.files
Expand Down
5 changes: 4 additions & 1 deletion spec/event-helpers.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget) ->
getData: (key) -> @data[key]
setDragImage: (@image) -> return

for entry in dragged
$(entry).addClass('selected')

dragStartEvent = $.Event()
dragStartEvent.target = dragged
dragStartEvent.currentTarget = dragged
Expand Down Expand Up @@ -39,4 +42,4 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) ->
for filePath in filePaths
dropEvent.originalEvent.dataTransfer.files.push({path: filePath})

dropEvent
dropEvent
67 changes: 64 additions & 3 deletions spec/tree-view-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3080,8 +3080,10 @@ describe "TreeView", ->
deltaFile = gammaDir[0].entries.children[2]

[dragStartEvent, dragEnterEvent, dropEvent] =
eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0])
eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.find('.header')[0])

treeView.onDragStart(dragStartEvent)
expect(deltaFile).toHaveClass('selected')
treeView.onDragEnter(dragEnterEvent)
expect(alphaDir).toHaveClass('selected')

Expand All @@ -3104,7 +3106,7 @@ describe "TreeView", ->
deltaFile = gammaDir[0].entries.children[2]

[dragStartEvent, dragEnterEvent, dropEvent] =
eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0], alphaDir[0])
eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.find('.header')[0], alphaDir[0])

runs ->
treeView.onDragStart(dragStartEvent)
Expand All @@ -3117,6 +3119,65 @@ describe "TreeView", ->
runs ->
expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3

describe "when dropping multiple FileViews onto a DirectoryView's header", ->
it "should move the files to the hovered directory", ->
# Dragging delta.txt onto alphaDir
alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
alphaDir[0].expand()

gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
gammaDir[0].expand()
gammaFiles = [].slice.call(gammaDir[0].entries.children, 1, 3)

[dragStartEvent, dragEnterEvent, dropEvent] =
eventHelpers.buildInternalDragEvents([gammaFiles], alphaDir.find('.header')[0], alphaDir[0])

runs ->
treeView.onDragStart(dragStartEvent)
treeView.onDrop(dropEvent)
expect(alphaDir[0].children.length).toBe 2

waitsFor "directory view contents to refresh", ->
$(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2

runs ->
expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 4

describe "when dropping a DirectoryView and FileViews onto a DirectoryView's header", ->
it "should move the files and directory to the hovered directory", ->
# Dragging alpha.txt and alphaDir into thetaDir
rootDir = $(treeView.roots[0])

alphaFile = rootDir[0].entries.children[2]
alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
alphaDir[0].expand()

gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
gammaDir[0].expand()
thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first')

dragged = [alphaFile, alphaDir[0]]

[dragStartEvent, dragEnterEvent, dropEvent] =
eventHelpers.buildInternalDragEvents(dragged, thetaDir.find('.header')[0], thetaDir[0])

runs ->
treeView.onDragStart(dragStartEvent)
treeView.onDrop(dropEvent)
expect(thetaDir[0].children.length).toBe 2

waitsFor "directory view contents to refresh", ->
$(treeView.roots[0].entries).find('.directory:contains(theta):first .entry').length > 2

runs ->
thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first')
thetaDir[0].expand()
expect(thetaDir.find('.entry').length).toBe 2
# alpha dir still has all its entries
alphaDir = $(thetaDir[0].entries).find('.directory:contains(alpha):first')
alphaDir[0].expand()
expect(alphaDir.find('.entry').length).toBe 2

describe "when dropping a DirectoryView onto a DirectoryView's header", ->
it "should move the directory to the hovered directory", ->
# Dragging thetaDir onto alphaDir
Expand All @@ -3128,7 +3189,7 @@ describe "TreeView", ->
thetaDir = gammaDir[0].entries.children[0]

[dragStartEvent, dragEnterEvent, dropEvent] =
eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.find('.header')[0], alphaDir[0])
eventHelpers.buildInternalDragEvents([thetaDir], alphaDir.find('.header')[0], alphaDir[0])

runs ->
treeView.onDragStart(dragStartEvent)
Expand Down