Showing posts with label drag and drop. Show all posts
Showing posts with label drag and drop. Show all posts

Monday, August 30, 2010

File Promises and dojo drag and drop

When Adobe pushed AIR 2.0 out via auto update, one of the first new API features that I wanted to try to take advantage of is the File Promises API. Armed with Christian Cantrell's article, I went about integrating File Promises into my application.

Drag and drop isn't a new interaction paradigm in the application I work on. Users have always been able to drag and drop items in the application thanks to dojo's drag and drop APIs. With AIR 2.0 and File Promises though, I wanted to give the user the ability to drag files internally in the application and also out of the application and onto their desktop. To allow both internal and external drag and drop operations, I needed to override the default dojo.dnd.Avatar. Overriding the default Avatar for dojo dnd operations is really easy - the constructor for your dnd source should include the following line (basically, you need some way of telling the dnd manager to use a different function when creating the avatar):
dojo.dnd.manager().makeAvatar = dojo.hitch(this, "makeAvatar");

You'll need a makeAvatar function which returns your custom Avatar:
makeAvatar: function() {
return new my.widget.FileListAvatar(dojo.dnd.manager());
},

Once you've told the dojo dnd manager to use your custom dnd avatar, you'll need to figure out when the avatar in question is being dragged close to the edge of the application. See the _handleMouseMove function below:

dojo.declare(
"my.widget.FileListAvatar",
[dojo.dnd.Avatar],
{
_mouseOverConnects: [],

construct: function() {
// override and cancel the autoScroll function to prevent whitespace from appearing
// in the application when the user is dragging files near the border.
dojo.dnd.autoScroll = function(){};
this._mouseOverConnects.push(dojo.connect(this.manager, "onMouseMove", this, "_handleMouseMove"));
}

_handleMouseMove: function() {
var pos, viewport = dojo.window.getBox();
if(this.node) {
pos = dojo.position(this.node);
if(viewport.w <= pos.x + pos.w || pos.x <= 0
|| viewport.h <= pos.y + pos.h || pos.y + pos.h <= 0) {
// at this stage we know that the user is dragging the avatar close to the application's boundaries.
// time to make a switch from an internal dnd operation to an external dnd.
this. _handleExternalDnd();
}
}

The _handleExternalDnd is the point in the application where we start using the AIR 2.0 file promises API. In the following code, all the items in my dnd source have a store and item property which contain information on the url and file name. The url property from each dnd item is passed to a new URLFilePromise object.
_handleExternalDnd: function() {
var f, url, cb = new air.Clipboard(), promises = [], vp = dojo.window.getBox(), isDraggedOut = false;
for(var j = 0, k = this.manager.nodes.length; j < k; j++) {
f = this.manager.source.getItem(this.manager.nodes[j].id).data;
url = f.store.getValue(f.item, "Url");
if(url !== "") {
var fp = new air.URLFilePromise();
fp.request = new air.URLRequest(url);
fp.relativePath = f.store.getValue(f.item, "FileName");
promises.push(fp);
}
}
cb.setData(air.ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises);
// the next line causes the switch from internal dnd to external dnd
air.NativeDragManager.doDrag(window.htmlLoader, cb);
air.NativeApplication.nativeApplication.activeWindow.stage.addEventListener(window.runtime.flash.events.NativeDragEvent.NATIVE_DRAG_UPDATE, function(evt) {
if(!isDraggedOut && evt.localX > vp.w) {
isDraggedOut = true;
} else if (isDraggedOut && evt.localX < vp.w) {
// TODO - this is the case where the file has been dragged out of the application and the user is dragging the file back in.
// would be nice at this stage to revert to revert back to an internal dnd operation.
}
});
dojo.style(this.node, "display", "none");
},

I've left some commented code in there which might give you hints of what I'd like to do when the user drags files back into the application after initially dragging them out. I haven't figured out how to cancel a native drag and drop operation just yet - if I do, I'll be sure to update this blog post.

That's pretty much it. Once the user drags files out of the application and into their native file system browser, the urls included in the URLFilePromise object will be downloaded. Check out some of the events you can connect to if you want to give your users a progress indicator of where their files are in terms of being downloaded. One point to note when using File Promises - users are only allowed to drag files from an AIR application into the default file browser. For example, the user cannot drag files directly from an Adobe AIR application into iPhoto or Lightroom. They need to drag the files into Finder first, wait for the files to be downloaded and then drag them into the file management application of their choice.

Looking to the future, it looks like this type of functionality will start to become available natively in the browser.

Some useful links which might help with further reading:

Getting started with dojo dnd
dojocampus' dnd articles
Advanced dojo dnd tutorial
CSS Ninja article on drag and drop
CSS Ninja article how gmail's dnd works

Sunday, September 13, 2009

dojo updates

You gotta give the dojo guys credit. Thanks to @uhop for following up on my previous blog post. Even though my bug was pretty edge case, its great to see the dojo community going the extra mile to fix it. Its small things like this which make all the difference.

On another dojo related note, I attended the DDD at AOL in Mountain View on Thursday. Always good times hanging out with the dojo crew. Some really cool stuff in the pipeline too. It was great to get some samples of the dojo/django collaboration happening with Tobias and Mike's drawing samples were really cool too - I didn't know stuff like that was possible in the browser. Looking forward to dojo 1.4 already. Rob Christiansen from Adobe AIR popped in too - sounds like there's a lot in the pipeline for AIR 2.0. Maybe the dair dojo project will get rejuvenated with the arrival of AIR 2.0 and dojo 1.4

Wednesday, September 2, 2009

dojo drag and drop in AIR

Looking at common.js in the dojo.dnd package, I see this nice utility function:
Paste your text here.dojo.dnd._isMac = navigator.appVersion.indexOf("Macintosh") >= 0;
dojo.dnd._copyKey = dojo.dnd._isMac ? "metaKey" : "ctrlKey";

dojo.dnd.getCopyKeyState = function(e) {
// summary: abstracts away the difference between selection on Mac and PC,
// and returns the state of the "copy" key to be pressed.
// e: Event: mouse event
return e[dojo.dnd._copyKey]; // Boolean
};

However, this doesn't seem to work in AIR (only tested running on mac so far).

Switching the code to look like:
dojo.dnd._isMac = navigator.appVersion.indexOf("Macintosh") >= 0;
dojo.dnd._isAir = navigator.appVersion.indexOf("AdobeAIR") >= 0;
dojo.dnd._copyKey = dojo.dnd._isMac && !dojo.dnd._isAir ? "metaKey" : "ctrlKey";

seems to do the trick