Class NativeDragAndDrop
Drag and drop through the operating system rather than inside the application.
Codename One has always had a lightweight drag and drop -- Component#setDraggable(boolean)
and Component#setDropTarget(boolean) -- which moves a rendered image around inside one
form. That never leaves the application, so it cannot drop a file on the desktop, cannot
carry text into another application's window, and cannot receive anything from one.
This class is the other half: it hands the drag to the operating system's own drag machinery,
using the same ClipboardContent a copy publishes as the payload. That is the whole idea --
a drag is a copy that the user aims with the pointer, so anything the application can already
put on the clipboard it can already drag out, and anything it can paste it can already accept
as a drop.
Dragging out
Label file = new Label("report.pdf");
file.setNativeDragOperation(NativeDragOperation.createFileDrag(
new String[] { FileSystemStorage.getInstance().getAppHomePath() + "report.pdf" }));
Dropping that on the desktop, on a mail composer or into a file manager copies the file,
because the receiving application asked for ClipboardContent#MIME_FILE and the drag
offered it. Offer several representations and every receiver takes the best one it
understands.
Receiving a drop
Container inbox = new Container();
inbox.setNativeDropTarget(true);
inbox.addNativeDropListener(e -> {
NativeDropEvent drop = (NativeDropEvent)e;
String[] files = drop.getFiles();
...
});
Where it works
Native drag and drop needs the platform to have it. Check #isSupported() before offering
the affordance, and #isDragOutsideApplicationSupported() before promising the user that a
drag can leave the application: a desktop can drop onto any other window, a tablet can drop
into another application beside it, and a phone in full screen has nowhere for a drag to go
even though drags within the application still work. Where nothing is supported the calls
here are harmless no-ops and the lightweight drag and drop is unaffected.
Threading
The gesture half runs on the event dispatch thread; the receiving half is called from whatever thread the platform hands the port. All of the shared state below is therefore guarded by one lock, and no callback into component or port code is ever made while holding it -- the framework's own event dispatch thread blocks on the platform's UI thread to paint on some ports, so a lock held across a callback is a deadlock waiting for the first drag.
-
Method Summary
Modifier and TypeMethodDescriptionstatic voiddragCompleted(int performedAction) Reports that the session started by#startDrag(com.codename1.ui.Component, com.codename1.ui.NativeDragOperation)has finished, whatever the outcome, so that a source offeringNativeDragOperation#ACTION_MOVElearns whether to delete its copy.static intdragEnter(int windowId, int x, int y, ClipboardContent content, int allowedActions) Reports that a native drag has entered one of the application's surfaces.static voiddragExit(int windowId) Reports that a native drag has left the application's surfaces without dropping.static intdragOver(int windowId, int x, int y, ClipboardContent content, int allowedActions) Reports that a native drag has moved over one of the application's surfaces, and answers whether it would be accepted here.static NativeDragOperationReports that the platform started a drag session on its own, for the operation the press prepared.static intdrop(int windowId, int x, int y, ClipboardContent content, int action) Delivers a native drop.static NativeDragOperationReturns the drag this application is currently running through the operating system, or null when it is not dragging.static booleanReturns true when a drag started here can be dropped outside the application: on the desktop, in a file manager or in another application's window.static booleanReturns true when this platform can drag and drop through the operating system at all.static intplannedDropAction(int windowId, int x, int y, ClipboardContent content, int action) The action a drop at this position would perform, without dispatching anything or disturbing the drag in progress.static booleanstartDrag(Component source, NativeDragOperation op) Starts a native drag immediately, for an application that decides on its own that a drag has begun -- from a long press, or a menu item -- rather than letting a component do it throughComponent#setNativeDragSource(boolean).
-
Method Details
-
isSupported
public static boolean isSupported()Returns true when this platform can drag and drop through the operating system at all. Where this is false every method here does nothing and reports failure, so no call site needs to be conditional -- but an application that shows a "drag me" affordance should hide it. -
isDragOutsideApplicationSupported
public static boolean isDragOutsideApplicationSupported()Returns true when a drag started here can be dropped outside the application: on the desktop, in a file manager or in another application's window.
This is narrower than
#isSupported(). A platform can route drags between components, and between this application's own windows, while still refusing to let one leave -- which is the normal state of affairs on a phone. -
startDrag
Starts a native drag immediately, for an application that decides on its own that a drag has begun -- from a long press, or a menu item -- rather than letting a component do it through
Component#setNativeDragSource(boolean).Call this on the event dispatch thread while the pointer is still down; a drag the user is not currently holding cannot be aimed and platforms reject it.
Parameters
-
source: the component the drag comes from, used for the default drag image and reported byNativeDragOperation#getSource(). May be null. -
op: what is being dragged
Returns
true when the operating system took the drag; false when the platform has no native drag and drop, refused to start a session, or is already running one
-
-
dragSessionStarted
Reports that the platform started a drag session on its own, for the operation the press prepared. Ports whose operating system owns the drag gesture -- where a long press, not the framework's own threshold, is what begins a drag -- call this instead of returning true from
com.codename1.impl.CodenameOneImplementation#startNativeDrag(com.codename1.ui.NativeDragOperation).Returns
the operation the session is carrying, or null when nothing was prepared -- in which case the port should refuse to start a session
-
getActiveDrag
Returns the drag this application is currently running through the operating system, or null when it is not dragging. A drop target uses this to tell a drag it started itself from one that arrived from elsewhere, whichNativeDropEvent#isLocal()reports. -
dragEnter
public static int dragEnter(int windowId, int x, int y, ClipboardContent content, int allowedActions) Reports that a native drag has entered one of the application's surfaces.
Parameters
-
windowId: the id of the window the drag is over, or zero for the main surface -
x: the pointer position within that surface -
y: the pointer position within that surface -
content: the representations the drag is offering -
allowedActions: the actions the source permits
Returns
the action a drop would perform right now, or
NativeDragOperation#ACTION_NONEwhen nothing under the pointer will take it -
-
dragOver
public static int dragOver(int windowId, int x, int y, ClipboardContent content, int allowedActions) Reports that a native drag has moved over one of the application's surfaces, and answers whether it would be accepted here.
Threading
The operating system needs the answer synchronously, while the framework's callbacks have to run on the event dispatch thread -- and blocking a native drag thread on the event dispatch thread deadlocks, because on some ports the event dispatch thread is itself waiting on that native thread to paint. So the target is resolved here, on the calling thread, from state that does not change under it, while
Component#nativeDragOver(com.codename1.ui.NativeDropEvent)and the listeners are dispatched asynchronously; the value returned is the one they produced for the previous event on this same target. A target that changes its mind therefore shows the user the new cursor one drag event late, which is a frame, and never blocks.A target that refuses a drop outright should say so through
Component#canAcceptNativeDrop(com.codename1.ui.ClipboardContent)or the accepted MIME list instead, both of which are consulted here and are therefore exact from the first event.Parameters
-
windowId: the id of the window the drag is over, or zero for the main surface -
x: the pointer position within that surface -
y: the pointer position within that surface -
content: the representations the drag is offering -
allowedActions: the actions the source permits
Returns
the action a drop would perform right now, or
NativeDragOperation#ACTION_NONE -
-
dragExit
public static void dragExit(int windowId) Reports that a native drag has left the application's surfaces without dropping.
Parameters
windowId: the id of the window the drag left, or zero for the main surface
-
drop
Delivers a native drop.
The content must be fully materialized before this is called: on most platforms the native transfer object is only readable inside the drop callback, so a port that hands over a lazy view of it delivers empty data by the time the event dispatch thread reads it.
Parameters
-
windowId: the id of the window dropped on, or zero for the main surface -
x: the pointer position within that surface -
y: the pointer position within that surface -
content: the dropped representations -
action: the action the operating system settled on
Returns
the action actually accepted, or
NativeDragOperation#ACTION_NONEwhen nothing under the pointer took the drop and the port should report the transfer as failed -
-
plannedDropAction
public static int plannedDropAction(int windowId, int x, int y, ClipboardContent content, int action) The action a drop at this position would perform, without dispatching anything or disturbing the drag in progress.
A port whose platform commits to an action before it can read the transferred data -- AWT does, because a drop has to be accepted before it becomes readable -- asks here first, so that what it commits to is what
#drop(int, int, int, com.codename1.ui.ClipboardContent, int)will go on to report. Committing the platform's own stale action instead told the source a copy had happened while the target was handed a move.Parameters
-
windowId: the id of the window the drag is over, or zero for the main surface -
x: the pointer position within that surface -
y: the pointer position within that surface -
content: the representations the drag is offering, which may still be a description rather than the materialized payload -
action: the action the platform is proposing
Returns
the action the drop would perform, or
NativeDragOperation#ACTION_NONE -
-
dragCompleted
public static void dragCompleted(int performedAction) Reports that the session started by
#startDrag(com.codename1.ui.Component, com.codename1.ui.NativeDragOperation)has finished, whatever the outcome, so that a source offeringNativeDragOperation#ACTION_MOVElearns whether to delete its copy.Parameters
performedAction: the action the receiver performed, orNativeDragOperation#ACTION_NONEwhen the drag was cancelled or refused
-