Class NativeDragAndDrop

java.lang.Object
com.codename1.ui.NativeDragAndDrop

public final class NativeDragAndDrop extends Object

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 Type
    Method
    Description
    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 offering NativeDragOperation#ACTION_MOVE learns whether to delete its copy.
    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.
    static void
    dragExit(int windowId)
    Reports that a native drag has left the application's surfaces without dropping.
    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.
    Reports that the platform started a drag session on its own, for the operation the press prepared.
    static int
    drop(int windowId, int x, int y, ClipboardContent content, int action)
    Delivers a native drop.
    Returns the drag this application is currently running through the operating system, or null when it is not dragging.
    static boolean
    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.
    static boolean
    Returns true when this platform can drag and drop through the operating system at all.
    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.
    static boolean
    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).

    Methods inherited from class Object

    clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • 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

      public static boolean startDrag(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 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 by NativeDragOperation#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

      public static NativeDragOperation 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

      public static NativeDragOperation 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, which NativeDropEvent#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_NONE when 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

      public static int drop(int windowId, int x, int y, ClipboardContent content, int action)

      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_NONE when 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 offering NativeDragOperation#ACTION_MOVE learns whether to delete its copy.

      Parameters
      • performedAction: the action the receiver performed, or NativeDragOperation#ACTION_NONE when the drag was cancelled or refused