Drag and Drop en Mac usando Cocoa

Desarrollar una aplicación que soporte Drag and Drop usando cocoa es algo bastante sencillo.
Antes de ver el drag and drop, hay que conocer lo que en Cocoa se conoce como Pasteboard.
En Mac, el Pasteboard es un servidor que se ejecuta como demonio en nuestro sistema. Concretamente el ejecutable esta en /usr/sbin/pboard. Dicho servidor nos proporciona un almacenamiento común a todas las aplicaciones, de tal manera que sea posible compartir datos entre ellas. Operaciones como el “Copy&Paste” o el “Drag&Drop” utilizan este servidor.
Desde el Foundation framework, para manipular este espacio, podemos utilizar la clase NSPasteboard. Dentro del servidor tenemos varios espacios identificados por un nombre, y dentro de cada espacio, podremos almacenar un dato asociados a su tipo.
A grandes rasgos, con el mensaje generalPasteboard obtendremos el pasteboard principal, y usando el metodo declareTypes:owner: informaremos de los tipos de dato que el receptor podrá encontrar en el pasteboard.
Finalmente utilizaremos los mensajes setData:forType: y dataForType: para leer y escribir en el pasteboard.
Hablando ya del drag and drop, recordemos que es una operación que involucra a dos partes, por un lado el origen que proporciona los datos, y por otro lado el receptor que recibe los datos y los interpreta.
La parte “emisora” de los datos quizás sea la mas compleja, ya que, para respetar el aspecto visual, debemos crear una imagen que informe al usuario de que estamos enviando a la aplicación receptora.
El primer paso, es indicar que operaciones nuestra vista va a soportar, cocoa sabe cuales son esas operaciones ya que lo devuelve el método que debemos implementar draggingSourceOperationMaskForLocal:
Algunos posibles valores para devolver son:
NSDragOperationCopy
NSDragOperationDelete
NSDragOperationMove
En la parte emisora, este método sólo dice lo que podemos ofrecer, es en la parte receptora donde se especifica el tipo de operación a realizar. Cuando se realice el drop, la parte emisora podrá saber que operación se realizó y así actualizar sus contenidos en conveniencia.
Por ejemplo, si arrastramos nuestro contenido a la papelera, la papelera solicitará una operación de tipo NSOperationDelete, por lo que la vista emisora deberia hacer desaparecer dicho contenido.
Para implementar la parte visual, debemos poner nuestro código en el método mouseDragged, dentro de este método debemos incluir una llamada al método de NSView dragImage:at:offset:event:pasteboard:source:sildeBack:
Cuando el drop se ha completado, a nuestra vista nos llega el mensaje draggedImage:endedAt:operation donde principalmente, dependiendo de la operación realizada debemos actualizar el contenido de nuestra vista.
En la parte receptora, la vista que va a recibir los datos, primeramente debe registrar los tipos que va a poder recibir. Para ello existe dentro de NSView la llamada registerForDraggedTypes: la cual recibe como parámetro un array de dichos tipos. Los tipos son muy diversos pero podemos destacar:
NSStringPboardType
NSURLPboardType
NSPDFPboardType
NSRTFPboardType
Como vemos, la parte en negrita nos especifica el tipo de datos.
Habitualmente lo mas apropiado es llamar a este metodo en el initWithFrame: de nuestra vista. Así por ejemplo, en nuestra aplicación de prueba el código sería:
[self registerForDraggedTypes:[NSArray arrayWithObject:NSStringPboardType]];
Una vez que hemos registrado los tipos de datos que aceptamos, el siguiente paso es implementar los métodos que controlan la operación:
draggingEntered:
draggingUpdated:
draggingExited:
prepareForDragOperation:
performDragOperation:
concludeDragOperation:
Como vemos, cada mensaje cubre una parte del ciclo del “drop”, los tres primeros controlan el hecho de que el contenido que viene desde la parte emisora entra en nuestra vista, y finalmente los últimos segmentan en tres partes la parte en la que realmente se completa el “drop”.
En los últimos métodos en ningún momento se nos pasa el dato que forma parte de la transacción, sino que nosotros mismos debemos ir al PBoard y leerlo de allí, normalmente esto se hace en el método performDragOperation:, por ejemplo el código para leer una cadena sería:
– (BOOL) performDragOperation:(id <NSDraggingInfo>) sender
{
NSPasteboard *pb = [sender draggingPasteboard];
if ( [[pb types] containsObject:NSStringPboardType] ) {
NSString *value = [pb stringForType:NSStringPboardType];
NSLog(@”Dropped value: %@”, value);
return YES;
}
return NO;
}

capturaDnD

Desarrollar una aplicación que soporte Drag and Drop usando cocoa es algo bastante sencillo.

Antes de ver el drag and drop, hay que conocer lo que en Cocoa se conoce como Pasteboard.

En Mac, el Pasteboard es un servidor que se ejecuta como demonio en nuestro sistema. Concretamente el ejecutable esta en /usr/sbin/pboard. Dicho servidor nos proporciona un almacenamiento común a todas las aplicaciones, de tal manera que sea posible compartir datos entre ellas. Operaciones como el “Copy&Paste” o el “Drag&Drop” utilizan este servidor.

Desde el Foundation framework, para manipular este espacio, podemos utilizar la clase NSPasteboard. Dentro del servidor tenemos varios espacios identificados por un nombre, y dentro de cada espacio, podremos almacenar un dato asociados a su tipo.

A grandes rasgos, con el mensaje generalPasteboard obtendremos el pasteboard principal, y usando el metodo declareTypes:owner: informaremos de los tipos de dato que el receptor podrá encontrar en el pasteboard.

Finalmente utilizaremos los mensajes setData:forType: y dataForType: para leer y escribir en el pasteboard.

Hablando ya del drag and drop, recordemos que es una operación que involucra a dos partes, por un lado el origen que proporciona los datos, y por otro lado el receptor que recibe los datos y los interpreta.

La parte “emisora” de los datos quizás sea la mas compleja, ya que, para respetar el aspecto visual, debemos crear una imagen que informe al usuario de que estamos enviando a la aplicación receptora.

El primer paso, es indicar que operaciones nuestra vista va a soportar, cocoa sabe cuales son esas operaciones ya que lo devuelve el método que debemos implementar draggingSourceOperationMaskForLocal:

Algunos posibles valores para devolver son:

  • NSDragOperationCopy
  • NSDragOperationDelete
  • NSDragOperationMove

En la parte emisora, este método sólo dice lo que podemos ofrecer, es en la parte receptora donde se especifica el tipo de operación a realizar. Cuando se realice el drop, la parte emisora podrá saber que operación se realizó y así actualizar sus contenidos en conveniencia.

Por ejemplo, si arrastramos nuestro contenido a la papelera, la papelera solicitará una operación de tipo NSOperationDelete, por lo que la vista emisora deberia hacer desaparecer dicho contenido.

Para implementar la parte visual, debemos poner nuestro código en el método mouseDragged, dentro de este método debemos incluir una llamada al método de NSView dragImage:at:offset:event:pasteboard:source:sildeBack:

Cuando el drop se ha completado, a nuestra vista nos llega el mensaje draggedImage:endedAt:operation donde principalmente, dependiendo de la operación realizada debemos actualizar el contenido de nuestra vista.

En la parte receptora, la vista que va a recibir los datos, primeramente debe registrar los tipos que va a poder recibir. Para ello existe dentro de NSView la llamada registerForDraggedTypes: la cual recibe como parámetro un array de dichos tipos. Los tipos son muy diversos pero podemos destacar:

  • NSStringPboardType
  • NSURLPboardType
  • NSPDFPboardType
  • NSRTFPboardType

Como vemos, la parte en negrita nos especifica el tipo de datos.

Habitualmente lo mas apropiado es llamar a este metodo en el initWithFrame: de nuestra vista. Así por ejemplo, en nuestra aplicación de prueba el código sería:

[self registerForDraggedTypes:[NSArray arrayWithObject:NSStringPboardType]];

Una vez que hemos registrado los tipos de datos que aceptamos, el siguiente paso es implementar los métodos que controlan la operación:

  • draggingEntered:
  • draggingUpdated:
  • draggingExited:
  • prepareForDragOperation:
  • performDragOperation:
  • concludeDragOperation:

Como vemos, cada mensaje cubre una parte del ciclo del “drop”, los tres primeros controlan el hecho de que el contenido que viene desde la parte emisora entra en nuestra vista, y finalmente los últimos segmentan en tres partes la parte en la que realmente se completa el “drop”.

En los últimos métodos en ningún momento se nos pasa el dato que forma parte de la transacción, sino que nosotros mismos debemos ir al PBoard y leerlo de allí, normalmente esto se hace en el método performDragOperation:, por ejemplo el código para leer una cadena sería:

- (BOOL) performDragOperation:(id <NSDraggingInfo>) sender
{
    NSPasteboard *pb = [sender draggingPasteboard];
    if ( [[pb types] containsObject:NSStringPboardType] ) {
        NSString *value = [pb stringForType:NSStringPboardType];
        NSLog(@"Dropped value: %@", value);
        return YES;
    }
    return NO;
}

Para ilustrar la entrada y completar el código que falta por especificar, desde aquí podeis bajaros un ejemplo de uso de drag and drop.

Si se arrastra el cuadrado negro de una vista a otra, vemos en el log de ejecución como la palabra “Prueba” llega desde la vista origen a la vista destino.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: