Skip to content

Commit 9d896d2

Browse files
committed
fix: preserve dropped-file activation order
Open dropped paths sequentially so a slower earlier file cannot steal focus from the last dropped file. Continue processing after individual failures and retain the existing error reporting. Cover a final image that is already open and a failed path followed by a valid image in the existing LegacyInteg suite. All seven drag-and-drop checks pass on native Windows, macOS and Linux. Update the Pro pin to include the matching investigation and verification notes.
1 parent e05628d commit 9d896d2

2 files changed

Lines changed: 33 additions & 4 deletions

File tree

‎src/utils/DragAndDrop.js‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ define(function (require, exports, module) {
9999
}
100100

101101
/**
102-
* Open dropped files
102+
* Open dropped files in order so the last file becomes active, regardless of load times.
103+
* Continue processing the remaining paths if an earlier file cannot be opened.
103104
*
104105
* @param {Array.<string>} paths Array of file paths dropped on the application.
105106
* @return {Promise} Promise that is resolved if all files are opened, or rejected
@@ -109,7 +110,8 @@ define(function (require, exports, module) {
109110
var errorFiles = [],
110111
ERR_MULTIPLE_ITEMS_WITH_DIR = {};
111112

112-
return Async.doInParallel(paths, function (path, idx) {
113+
// Each open activates its view, so parallel opens would select whichever finishes last.
114+
return Async.doSequentially(paths, function (path, idx) {
113115
var result = new $.Deferred();
114116

115117
// Only open files.

‎test/spec/DragAndDrop-integ-test.js‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
*
2020
*/
2121

22-
/*global describe, beforeEach, afterEach, it, expect, awaitsForDone, beforeAll, afterAll */
22+
/*global describe, beforeEach, afterEach, it, expect, awaitsForDone, awaitsForFail, beforeAll, afterAll, spyOn */
2323

2424
define(function (require, exports, module) {
2525

2626

2727
// Load dependent modules
28-
var DocumentManager, // loaded from brackets.test
28+
let DocumentManager, // loaded from brackets.test
29+
Dialogs, // loaded from brackets.test
2930
DragAndDrop, // loaded from brackets.test
3031
EditorManager, // loaded from brackets.test
3132
MainViewManager, // loaded from brackets.test
@@ -45,6 +46,7 @@ define(function (require, exports, module) {
4546

4647
// Load module instances from brackets.test
4748
DocumentManager = testWindow.brackets.test.DocumentManager;
49+
Dialogs = testWindow.brackets.test.Dialogs;
4850
DragAndDrop = testWindow.brackets.test.DragAndDrop;
4951
EditorManager = testWindow.brackets.test.EditorManager;
5052
MainViewManager = testWindow.brackets.test.MainViewManager;
@@ -53,6 +55,7 @@ define(function (require, exports, module) {
5355
afterAll(async function () {
5456
testWindow = null;
5557
DocumentManager = null;
58+
Dialogs = null;
5659
DragAndDrop = null;
5760
EditorManager = null;
5861
MainViewManager = null;
@@ -115,6 +118,30 @@ define(function (require, exports, module) {
115118
expect(MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE)).toEqual(lastImagePath);
116119
});
117120

121+
it("should leave the last dropped image active even when it is already open", async function () {
122+
const firstImagePath = testPath + "/couz.png";
123+
const lastImagePath = testPath + "/couz2.png";
124+
await awaitsForDone(DragAndDrop.openDroppedFiles([lastImagePath]), "opening the last image first");
125+
await awaitsForDone(DragAndDrop.openDroppedFiles([firstImagePath, lastImagePath]),
126+
"opening a drop ending with the already open image");
127+
128+
expect(MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE)).toEqual(lastImagePath);
129+
expect(MainViewManager.findInWorkingSet(MainViewManager.ALL_PANES, firstImagePath)).not.toEqual(-1);
130+
expect(MainViewManager.findInWorkingSet(MainViewManager.ALL_PANES, lastImagePath)).not.toEqual(-1);
131+
});
132+
133+
it("should open later dropped files and report an earlier file that cannot be opened", async function () {
134+
const imagePath = testPath + "/couz2.png";
135+
// Keep the error presentation out of the way while checking the failed drop's result.
136+
const errorDialog = spyOn(Dialogs, "showModalDialog").and.stub();
137+
await awaitsForFail(DragAndDrop.openDroppedFiles([testPath + "/missing-drop-file.png", imagePath]),
138+
"reporting the missing file after processing the rest of the drop");
139+
140+
expect(MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE)).toEqual(imagePath);
141+
expect(MainViewManager.findInWorkingSet(MainViewManager.ALL_PANES, imagePath)).not.toEqual(-1);
142+
expect(errorDialog.calls.count()).toBe(1);
143+
});
144+
118145
it("should add images to the working set when they dropped from outside the project", async function () {
119146
var imagesPath = SpecRunnerUtils.getTestPath("/spec/test-image-files");
120147
var files = [imagesPath + "/thermo.jpg", imagesPath + "/eye.jpg"];

0 commit comments

Comments
 (0)