Otros mensajes de ratón

Algunas de las características que ahora son corrientes en el manejo del ratón, no lo eran o ni siquiera existían hace unos años. Por ejemplo, la rueda del ratón es un invento relativamente reciente. Además, debido a los navegadores de Internet, se han popularizado algunos eventos relacionados con el ratón que antes no existían, como el paso sobre zonas determinadas, o la transición entre unas zonas y otras de la pantalla. Estos eventos se usan en los navegadores para cambiar el aspecto de textos o botones, y de ese modo llamar la atención del usuario sobre ellos para indicar que son zonas activas a clics.

Veamos ahora estos nuevos mensajes en el API.

Mensaje WM_MOUSEWHEEL (Windows NT)

El mensaje WM_MOUSEWHEEL se envía cada vez que el usuario activa la rueda de desplazamiento del ratón. Los parámetros de este mensaje son los mismos que en el mensaje WM_MOUSEMOVE, pero para incluir la información sobre el avance o retroceso de la rueda, el parámetro wParam se ha dividido en dos. En la parte baja se empaquetan las banderas sobre el estado de los botones, y en la parte alta el valor de avance o retroceso de la rueda. Usaremos las macros LOWORD y HIWORD para extraer esos valores.

Los valores de avance y retroceso de la rueda serán siempre múltiplos de 120. Esto se ha hecho así para permitir que en el futuro se puedan construir dispositivos compatibles con la rueda, pero que proporcionen mayor precisión. Debemos considerar siempre que una unidad de desplazamiento de la rueda es 120, o para evitar problemas de dependencias, de la constante WHEEL_DELTA.

Los valores positivos indican movimientos de rueda hacia adelante, y los negativos, hacia atrás.

        case WM_MOUSEWHEEL:
           punto = MAKEPOINTS(lParam);
           izq = (LOWORD(wParam) & MK_LBUTTON) ? 1 : 0;
           cen = (LOWORD(wParam) & MK_MBUTTON) ? 1 : 0;
           der = (LOWORD(wParam) & MK_RBUTTON) ? 1 : 0;
           rueda = HIWORD(wParam);
           rueda /= WHEEL_DELTA;
           hdc = GetDC(hwnd);
           Pintar(hdc, izq, cen, der, punto, TRUE);
           sprintf(cad, "rueda = %04d ", rueda);
           TextOut(hdc, 10, 130, cad, strlen(cad));
           ReleaseDC(hwnd, hdc);
           break;

Trazar eventos del ratón (Windows NT)

Nuestra aplicación puede recibir dos mensajes sobre eventos del ratón: WM_MOUSELEAVE y WM_MOUSEHOVER, cuando el ratón abandona una ventana o cuando permanece sobre un área determinada de la ventana durante un periodo de tiempo, respectivamente. Pero para que esto suceda, previamente debemos activar el trazado de eventos, mediante una llamada a la función TrackMouseEvent.

Esta función necesita como parámetro un puntero a una estructura TRACKMOUSEEVENT. En esa estructura indicamos qué eventos queremos que se notifique, y el tiempo necesario para generar un mensaje WM_MOUSEHOVER:

        case WM_NCHITTEST:
           hittest = DefWindowProc(hwnd, msg, wParam, lParam);
           if(HTCLIENT == hittest) {
              if(!dentro) {
                 dentro = TRUE; 
                 tme.cbSize = sizeof(TRACKMOUSEEVENT);
                 tme.dwFlags = TME_HOVER | TME_LEAVE;
                 tme.hwndTrack = hwnd;
                 tme.dwHoverTime = 1000;
                 TrackMouseEvent(&tme);
              }
           }    
           return hittest;
           break;

En este ejemplo llamamos a TrackMouseEvent para que nos notifique ambos eventos para la ventana hwnd, y ajustamos el tiempo Hover en un segundo.

Mensaje WM_MOUSELEAVE (Windows NT)

Si hemos activado el evento Leave recibiremos un mensaje WM_MOUSELEAVE cuando el cursor abandone el área de cliente de la ventana. Una vez que se recibe el mensaje no se vuelve a generar, salvo que volvamos a activar el evento, usando de nuevo la función TrackMouseEvent.

        case WM_MOUSELEAVE:
           dentro = FALSE; 
           hdc = GetDC(hwnd);
           TextOut(hdc, 10, 170, "Leave", 5);
           ReleaseDC(hwnd, hdc);
           break;

Mensaje WM_MOUSEHOVER (Windows NT)

Del mismo modo, si hemos activado el evento Hover recibiremos un mensaje WM_MOUSEHOVER cuando haya transcurrido el tiempo indicado y el cursor no se haya movido de una zona determinada del área de cliente de la ventana. Esta zona está predederminada por Windows, aunque se puede modificar su tamaño (ver TRACKMOUSEEVENT. Una vez que se recibe el mensaje no se vuelve a generar, salvo que volvamos a activar el evento, usando de nuevo la función TrackMouseEvent.

        case WM_MOUSEHOVER:
           hdc = GetDC(hwnd);
           TextOut(hdc, 10, 170, "Hover", 5);
           ReleaseDC(hwnd, hdc);
           break;

Ejemplo 34


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 34 win034.zip 2004-07-06 5809 bytes 251

Arrastrar objetos

Una de las operaciones más frecuentes que se realizan mediante el ratón es la de arrastrar objetos. No entraremos en muchos detalles por ahora, Windows dispone de formas especiales de realizar arrastre de objetos entre distintas ventanas y aplicaciones, pero de momento veremos un ejemplo sencillo sobre cómo arrastrar objetos dentro de una misma ventana.

En este ejemplo usaremos iconos como objetos. A cada icono le corresponde una imagen y una posición en pantalla:

typedef struct {
    HICON icono;
    POINT coordenada;
} Objeto;

Al procesar el mensaje WM_CREATE inicializamos el array de objetos:

        case WM_CREATE:
           hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
           objeto[0].icono = LoadIcon(hInstance, "ufo");
           objeto[0].coordenada.x = 10;
           objeto[0].coordenada.y = 10;
           objeto[1].icono = LoadIcon(hInstance, "libro");
           objeto[1].coordenada.x = 10;
           objeto[1].coordenada.y = 50;
           objeto[2].icono = LoadIcon(hInstance, "mundo");
           objeto[2].coordenada.x = 10;
           objeto[2].coordenada.y = 90;
           objeto[3].icono = LoadIcon(hInstance, "hamburguesa");
           objeto[3].coordenada.x = 50;
           objeto[3].coordenada.y = 10;
           objeto[4].icono = LoadIcon(hInstance, "smile");
           objeto[4].coordenada.x = 50;
           objeto[4].coordenada.y = 50;
           capturado = -1;
           break;

Una operación de arrastre comienza con un clic sobre un objeto. Después, sin soltar el botón del ratón, se desplaza el ratón, y con él el obejto, a la posición deseada, y finalmente, se suelta el botón del ratón en esa posición.

Lo primero es seleccionar el objeto a arrastrar. Para ello procesaremos el mensaje WM_LBUTTONDOWN, y comprobaremos si las coordenadas del cursor corresponden con alguno de los objetos que es posible arrastrar. Si es así, guardamos en una variable el identificador del objeto, y al mismo tiempo, activamos el modo de arrastre:

        case WM_LBUTTONDOWN:
           punto = MAKEPOINTS(lParam);
           for(i = 0; capturado == -1 && i < 5; i++) {
              SetRect(&re,
                objeto[i].coordenada.x,
                objeto[i].coordenada.y,
                objeto[i].coordenada.x+32,
                objeto[i].coordenada.y+32);
              POINTSTOPOINT(lpunto, punto);
              if(PtInRect(&re, lpunto)) {
                 capturado = i;
                 ClientToScreen(hwnd, &objeto[i].coordenada);
                 SetCursorPos(objeto[i].coordenada.x,
                    objeto[i].coordenada.y);
                 SetCapture(hwnd);
                 ShowCursor(FALSE);
              }
           }    
           break;

Si la variable capturado vale -1, indica que no se está realizando un arrastre, si tiene otro valor, indica el objeto arrestrado.

Para cada objeto calculamos las coordenadas de un rectángulo que lo contiene, y comprobamos si la posición actual del cursor está dentro del rectángulo. Si es así, actualizamos el valor de capturado, cambiamos la posición del cursor a la esquina superior izquierda del objeto, y ocultamos el cursor.

El cambio de coordenadas sirve para eliminar el salto que se produciría cuando pinchemos sobre un punto distinto de la esquina superior izquierda. Ocultar el cursor hace que el objeto arrastrado parezca sustituir al cursor, y hace más fácil arrastrarlo con precisión.

Además, capturamos el ratón para que la operación de arrastre no se interrumpa si el cursor sale del área de cliente de la ventana.

Durante el arrastre debemos procesar el mensaje WM_MOUSEMOVE. Cada vez que recibamos el mensaje, borraremos el objeto en su posición actual, actualizamos las coordenadas según la posición del cursor, y volvemos a dibujarlo en la nueva posición:

        case WM_MOUSEMOVE:
           punto = MAKEPOINTS(lParam);
           if(capturado != -1) {
              hdc = GetDC(hwnd);
              //drag
              // Borrar en posición actual:
              SetRect(&re,
                 objeto[capturado].coordenada.x,
                 objeto[capturado].coordenada.y,
                 objeto[capturado].coordenada.x+32,
                 objeto[capturado].coordenada.y+32);
              FillRect(hdc, &re, GetSysColorBrush(COLOR_BACKGROUND));
              // Actualizar coordenadas:
              POINTSTOPOINT(objeto[capturado].coordenada, punto);
              // Pintar en nueva posición:
              DrawIcon(hdc,
                 objeto[capturado].coordenada.x,
                 objeto[capturado].coordenada.y,
                 objeto[capturado].icono);
              ReleaseDC(hwnd, hdc);
              InvalidateRect(hwnd, &re, FALSE);
           }
           break;

Para borrar pintamos usando el color de fondo, la zona ocupara por el objeto. Para actualizar la posición del objeto usamos la macro POINTSTOPOINT, esto es porque la posición del cursor se almacena en una estructura POINTS, y la del objeto en una estructura POINT. A continuación mostramos el objeto, e invalidamos la zona que ocupaba originalmente. Esto último es necesario, ya que el objeto arrastrado puede pasar sobre otros objetos borrándolos.

Finalmente, cuando soltemos el botón se recibirá un mensaje WM_LBUTTONUP. En ese momento debemos dar por terminada la operación de arrastre, y regresaremos al estado inicial:

        case WM_LBUTTONUP:
           if(capturado != -1) {
              capturado = -1;
              InvalidateRect(hwnd, NULL, FALSE);
              ReleaseCapture();
              ShowCursor(TRUE);
           }
           break;

Asignamos -1 a capturado, redibujamos toda la ventana, libreramos el ratón y mostramos el cursor.

Ejemplo 35


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 35 win035.zip 2004-07-06 7833 bytes 264