Site hosted by Angelfire.com: Build your free website today!

Tutorial 4: Pintando Textos

En este tutorial, aprenderemos como "pintar" texto en el área cliente de una ventana. También aprenderemos sobre el contexto del dispositivo.

Puedes bajar el código fuente desde aquí.

Teoría:

El texto en Windows es un tipo de objeto GUI.  Cada carácter está compuesto por numerosos pixeles o puntos (dots) que están amontonados dentro de un patrones distintos. Por eso hablamos de "pintar" en vez de "escribir". Normalmente, pintas texto en tu propia area cliente (relamente, puedes también pintar fuera del area cliente, pero eso es otra historia).  En Windows, poner texto en la pantalla es algo radicalmente distinto a DOS. En DOS, puedes pensar en la pantalla como una dimensión de 80x25. Pero en Windows, la pantalla es compartida por varios programas. Algunas reglas deben ser reforzadas para evitar que los programas escriban sobre la pantalla de otros. Windows asegura esto limitando el área para pintar de cada ventana a su área cliente solamente. El tamaño del area cliente de una ventana tampoco es constante. El usuario puede cambiarla en cualquier momento. Así que hay que determinar dinámicamente las dimensiones del área cliente de las ventanas.

Antes de que puedas pintar algo sobre el área cliente, debes pedir permiso a Windows. Eso es correcto, ya no tienes el control total sobre el monitor como lo tenías con DOS.  Debes pedir permiso a Windows para pintar tu propia area cliente. Windows determinará el tamaño de tu área cliente, de la fuente, los colores y otros atributos GDI y regresará un manejador del contexto de dispositivo a tu programa.

Luego puedes emplear tu contexto de dispositivo como un pasaporte para pintar tu área cliente.

¿Qué es un contexto de dispositivo? Es sólo una estructura de datos que Windows mantiene en su interior. Un contexto de dispositivo está asociado con un dispositivo en particular, tal como una impresora o un monitor de video. Para un monitor de video, un contexto de dispositivo está normalmente asociado con una ventana particular en el monitor.

Algunos valores en el contexto de dispositivo son atributos gráficos como colores, fuentes etc. Estos son valores por defecto que se pueden cambiar a voluntad. Existen para ayudar a reducir la carga de tener que especificar estos atributos en todas las llamadas a funciones GDI.

Puedes pensar en un contexto de dispositivo como un ambiente por defecto preparado para tí por Windows. Luego puedes anular algunos de los elementos establecidos por defecto si quieres.

Cuando un programa necesita pintar, debe obtener un manejador al contexto de dispositivo. Normalmente, hay varias maneras de realizar esto.

Hay algo que debes recordar para después de que tengas el manejador [handle] del contexto de dispositivo, y que debes realizar para el procesamiento de cualquier mensaje: no obtener el manejador en respuesta a un mensaje y emplearlo como respuesta a otro.

Windows envía mensajes WM_PAINT a la ventana para notificar que es ahora el momento de volver a pintar su área cliente. Windows no salva el contenido del área cliente de una ventana. En vez de eso, cuando ocurre una situación que garantiza que se va a volver a pintar el área cliente (tal como cuando una ventana ha sido cubierta por otra y luego descubierta), Windows pone el mensaje WM_PAINT en la cola de mensajes de ese programa. Es responsabilidad de Windows volver a pintar su propia área cliente. Debes reúnir toda la información sobre cómo volver a pintar el área cliente en la sección WM_PAINT de tu procedimiento de ventana, así que tu procedimiento de ventana puede volver a pintar tu area cliente cuando llega el mensaje WM_PAINT.

Otro concepto que debes tener en consideración es el de rectángulo inválido. Windows define un rectángulo inválido como el área rectangular más pequeña que el área cliente necesita para volver a ser pintada. Cuando Windows detecta un rectángulo inválido en el área cliente de una ventana, envía un mensaje WM_PAINT a esa ventana. En respuesta al mensaje WM_PAINT, la ventana puede obtener una estructura paintstruct que contiene, entre otras cosas, la coordenada del rectángulo inválido. Puedes llamar a BeginPaint en respuesta al mensaje WM_PAINT para validar el rectángulo inválido. Si no procesas el mensaje WM_PAINT, al menos debes llamar a DefWindowProc o a ValidateRect para validar el rectángulo inválido, sino Windows te enviará repetidamente el mensaje WM_PAINT.

Estos son los pasos que deberías realizar en respuesta a un mensaje WM_PAINT:

Nota que no tienes que validar explícitamente el rectángulo inválido. Esto es realizado automáticamente por la llamada a BeginPaint. Entre las llamadas a BeginPaint y EndPaint, puedes llamar cualquiera de las funciones GDI para pintar tu área. Casi todas ellas requieren el manejador del contexto de dispositivo como parámetro.

Contenido:

Escribiremos un programa que despliega una cadena con el texto "Win32 assembly is great and easy!" en el centro del área cliente.
 
.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

.DATA
ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0
OurText  db "Win32 assembly is great and easy!",0

.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.CODE
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke GetCommandLine
    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
        .WHILE TRUE
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
        .ENDW
        mov     eax,msg.wParam
        ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    LOCAL rect:RECT
    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_PAINT
        invoke BeginPaint,hWnd, ADDR ps
        mov    hdc,eax
        invoke GetClientRect,hWnd, ADDR rect
        invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
                DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,hWnd, ADDR ps
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor   eax, eax
    ret
WndProc endp
end start

Análisis:

La mayoría del código es el mismo que el del ejemplo del tutorial 3. Sólo explicaré los cambios importantes.

    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    LOCAL rect:RECT

Estas variables locales son usadas por las funciones GDI en tu sección WM_PAINT. hdc es usado para almacenar el manejador al contexto de dispositivo regresado por la llamada a BeginPaint. ps es una estructura PAINTSTRUCT. Normalmente no tienes que usar los valores en ps. Es pasado a la función BeginPaint y Windows la llena con valores apropiados. Luego pasa ps a la función EndPaint cuando terminas de pintar el área cliente. rect es una estructura RECT definida así:
 

RECT Struct
    left           LONG ?
    top           LONG ?
    right        LONG ?
    bottom    LONG ?
RECT ends
left y top son las coordenadas de la esquina izquierda superior de un rectángulo. right y bottom son las coordenadas de la esquina derecha inferior. Debe recordarse que: El origen de los ejes x-y está en la esquina superior izquierda. Entonces el punto y=10 está DEBAJO del punto y=0.

        invoke BeginPaint,hWnd, ADDR ps
        mov    hdc,eax
        invoke GetClientRect,hWnd, ADDR rect
        invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
                DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,hWnd, ADDR ps

En respuesta al mensaje WM_PAINT, llamas a BeginPaint pasando como parámetros al manejador de la ventana que quieres pintar y una estructura PAINTSTRUCT no inicializada. Después de una llamada exitosa, eax contiene el manejador al contexto de dispositivo. Luego llamas a GetClientRect para recobrar la dimensión del área cliente. La dimensión es regresada en la variable rect variable que tú pasas a DrawText como uno de sus parámetros. La sintaxis de DrawText es:

DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD

DrawText es una función de la API de alto-nivel para salida de texto. Maneja algunos detalles tales como ajuste de línea, centramiento, etc. así que puedes concentrarte sólo en la cadena que quieres pintar. Su hermana de bajo nivel, TextOut, será examinada en el próximo tutorial. DrawText formatea una cadena de texto para fijar dentro de los límites de un rectángulo. Emplea la fuente seleccionada en el momento, color y fondo (en el contexto de dispositivo) para dibujar texto. Las líneas son ajustadas para fijarla dentro de los límites del rectángulo. Regresa la altura del texto de salida en unidades de dispositivo, en nuestro caso, pixeles. Veamos sus parámetros:

Después de terminar de pintar el área cliente, debes llamar a la función EndPaint para liberar el manejador del contexto de dispositivo.

Eso es todo. Podemos hacer un sumario de los puntos relevantes:


Índice

Siguiente

[Iczelion's Win32 Assembly HomePage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por:   n u M I T_o r  [ kUT ]