Zoom 4, gestión del teclado

Esta última entrada sobre el zoom no trata sobre gráficos, sino sobre la gestión del teclado. Ya en otra ocasión traté sobre la Gestión de eventos de teclado. En aquella ocasión explique el sistema general de gestión de eventos en Cocoa y como recibir los carácteres para tratarlos en nuestro programa.

En esta ocasión quiero explicar cómo gestionar dos tipos de pulsaciones de teclado: las teclas de cursor y los atajos de teclado. En cualquier caso la mejor forma de tratar con el teclado es sobreescribir las funciones predefinidas en Cocoa.

Aceptar eventos de teclado

Lo primero siempre es informar al sistema que la vista en la que estamos es capaz de responder a los eventos. Para ello hay que sobreescribir la función acceptsFirstResponder.

- (BOOL)acceptsFirstResponder {
	// Permite que la vista tenga el foco.
	return YES;
}

Gestionar las pulsaciones de teclado

El siguiente paso es ser capaz de saber que hacer cuando se pulsa una tecla. Es decir hace falta sobreescribir la función keyDown:(NSvent).

- (void)keyDown:(NSEvent *)evento {
    // Las teclas de cursors estan asociadas al teclado numérico
    if ([evento modifierFlags] & NSNumericPadKeyMask) {
        [self interpretKeyEvents:[NSArray arrayWithObject:evento]];
    } else {
        [super keyDown:evento];
    }
}

Y es aquí dónde comienzan las cosas interesantes. Lo más importante és la llamada a la función [self interpretKeyEvents:(NSArray)] esto le indica al sistema que nuestra vista es capaz de gestionar los eventos de teclado. A cambio el sistema gestionará los atajos de teclado definidos en las preferencias del sistema y nos devolverá a nuestra vista los otros eventos que ocurran bien clasificados y definidos en la clase NSResponder.

Claro está que, en este momento, no nos interesa controlar todos los eventos de teclado, sino solamente aquellos relacionados con las teclas de cursor. Por ellos sólo se llama a la función interpretKeyEvents: si detectamos que los modificadores de estado (modifierFlags) nos indican a través de una máscara definida igualmente en NSResponder que estamos ante la pulsación de una tecla de cursor. Estos modificadores de estado nos marcan si hay alguna tecla especial pulsada (función, comando, alternativa, ..). Y curiosamente las teclas de cursor estan relacionadas con las teclas del teclado numérico (¿una herencia del IBM PC?). En caso que sea un evento que no nos interesa gestionar (cualquier otra pulsación de teclas) lo enviamos a la superclase para que se encarge.

Las teclas de cursor

El tercer paso es crear (sobreescribir) las funciones necesarias para gestionar las pulsaciones sobre las teclas de cursor. Cómo en este ejemplo quiero hacer que la vista vaya desplazando la ventana visible del scroll las funciones cambiarán el origen de scroll de la vista.

-(IBAction)moveUp:(id)sender
{
    NSPoint posicion=[self visibleRect].origin;
    posicion.y = posicion.y + 10;
    [self scrollPoint:posicion];
}

-(IBAction)moveDown:(id)sender
{
    NSPoint posicion=[self visibleRect].origin;
    posicion.y = posicion.y - 10;
    [self scrollPoint:posicion];
}

-(IBAction)moveLeft:(id)sender
{
    NSPoint posicion=[self visibleRect].origin;
    posicion.x = posicion.x - 10;
    [self scrollPoint:posicion];
}

-(IBAction)moveRight:(id)sender
{
    NSPoint posicion=[self visibleRect].origin;
    posicion.x = posicion.x + 10;
    [self scrollPoint:posicion];
}

Las funciones moveUp:, moveDown:, moveLeft: y moveRight: estan definadas (dónde sino) en NSResponder.

Atajos de teclado

Lo anterior no sirve si nos interesa definir atajos de teclado (key Bindings?). En esta ocasión queria reproducir el comportamiento de muchas aplicaciones de Apple, en las cuales apretando la combinación cmd+ se amplia la vista y con cmd- se reduce.

Pues resulta que los eventos que utilizan la teecla de comando no son eventos de teclado normales, sino que se tratan como pulsaciones equivalentes a una orden (en inglés performKeyEquivalent:). Entonces hay que buscarse la vida con esta nueva (para mi) función. Al igual que la orden interpretKeyEvents: hay que informar al sistema que la vista que estamos programando se puede encargar de ciertos eventos y dejar el resto para la superclase. Para ello la función ha de devolver un valor verdadero (YES) si es capaz de gestionar el evento.

El ejemplo es bastante más claro:

/**
* performKeyEquivalent:(NSEvent)
*
* Gestiona los atajos de teclado. Si no corresponde a uno de los gestionados
* lo reenvia a la superclase para la gestión.
*
*/
- (BOOL)performKeyEquivalent:(NSEvent *)evento {
	NSString *caracteres = [evento charactersIgnoringModifiers];
	unichar keyChar = ([caracteres characterAtIndex: 0]);
	BOOL retVal = NO;
	switch(keyChar) {
        float nuevaEscala = 0;
		case '+':
            nuevaEscala = factorEscala*2;
            [self setFactorEscala:nuevaEscala];
			retVal = YES;
			break;
        case '-':
            nuevaEscala = factorEscala/2;
            [self setFactorEscala:nuevaEscala];
			retVal = YES;
			break;
		default:
			retVal = [super performKeyEquivalent:evento];
	}
	return retVal;
}

La función recoge el evento y si la tecla pulsada és un “+” o un “-” amplia o reduce la escala. En caso contrario llama a la superclase para gestionar el evento. La sencillez de la función no tiene nada que ver con el tiempo que he tenido que dedicar a investigar estos atajos de teclado. Al final encontre la solución en uno de los ejemplos de la documentación de Apple AXCanvas (/Developer/Accessibility/AXCanvas).

Más información en

– Apple: Cocoa Event-Handling Guide.

Entradas Relacionadas

Etiquetas: , , ,

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: