Tutorial 4: Dessiner du texte
(Painting
with Text)
Dans ce tutorial, nous apprendrons comment "peindre" le texte dans 'le secteur client' (la zone de travail) d'une fenêtre. Nous verrons aussi tout le contexte autour de cette fonction. Vous pouvez télécharger le code source ici.
Théorie:
Le texte dans Windows est un type d'objet du GUI (NdT : Graphic User Interface). Chaque caractère est composé de plusieurs pixels (les points) qui sont regroupés dans un ordre particulier. C'est pourquoi on peut appeler "painting" au lieu de writting". Normalement, vous peignez le texte dans votre propre secteur client (en réalité, vous pouvez peindre le secteur client extérieur mais c'est une autre histoire). L'affichage du texte sur l'écran sous Windows diffère résolument du DOS. Sous DOS, la dimension de l'écran est 80x25. Mais sous Windows, l'écran est partagé par plusieurs programmes. Des règles doivent être prises pour éviter que certains programmes s'affichent par dessus l'application d'autres programmes. Windows contrôle ceci en limitant la peinture du secteur de chaque fenêtre à son propre secteur client seulement. La taille du secteur client d'une fenêtre n'est pas aussi rigide. L'utilisateur peut changer sa taille n'importe quand. Donc vous devez déterminer les dimensions de votre propre secteur client.
Avant que vous ne puissiez peindre quelque chose sur le secteur client, vous devez demander la permission à Windows. C'est ça, vous n'avez pas le contrôle absolu de l'écran comme si vous étiez sous DOS. Vous devez demander à Windows la permission de peindre votre propre secteur client. Windows déterminera la taille de votre secteur client, la taille des caractères, les couleurs et les autres GDI attribués et ensuite renvoie à votre programme l'Handle du 'contexte de ce dispositif' (de tout ça). Vous pourrez alors employer le 'contexte de dispositif' comme un passeport à la peinture sur votre 'secteur client'.
C'est quoi le 'contexte de dispositif' ? C'est juste une structure de données conçue à intérieure de Windows (pas accessible pour nous). Un 'contexte de dispositif' est associé à un dispositif particulier, comme une imprimante ou l'affichage vidéo. Pour une exposition vidéo, un contexte de dispositif est d'habitude associé à une fenêtre particulière en exposition.
Quelques-unes des valeurs dans le contexte de dispositif sont des attributs graphiques comme des couleurs, la font etc. Ce sont des valeurs par défaut que vous pouvez changer à volonté. Il est possible pour réduire la charge de travail de spécifier ces attributs dans chaques appels de fonction GDI.
Vous pouvez vous représenter un 'contexte de dispositif' comme un 'environnement par défaut' mis à votre disposition par Windows. Vous pouvez ne pas tenir compte de certains éléments par défaut puis y revenir plus tard si vous le souhaitez.
Quand un programme a besoin de peindre, il doit obtenir un 'Handle de contexte de dispositif'. Normalement, il y a plusieurs façons de l'obtenir.
Appelez BeginPaint en réponse au message WM_PAINT.
Appelez GetDC en réponse à d'autres messages.
Appelez CreateDC pour créer votre propre contexte de dispositif.
Rappeler vous une chose, après avoir obtenu un 'Handle de contexte de dispositif', vous devez le sortir pendant le traitement d'un simple message. Ne récupérez pas un Handle en réponse à un message, transférez-le en réponse à un autre message.
Windows envoie des messages WM_PAINT à une fenêtre pour lui indiquer qu'il est maintenant temps de repeindre son secteur client. Windows ne sauve pas le contenu du secteur client de la fenêtre, mais au lieu de cela, quand une situation arrive et garantit une nouvelle peinture du secteur client (comme quand une fenêtre a été couverte par une autre et vient la recouvrir), Windows met le message WM_PAINT dans la file d'attente des messages de cette fenêtre. C'est à cette fenêtre de repeindre son propre secteur client. Vous devez réunir toute l'information qui sert à repeindre votre secteur client dans la section WM_PAINT de votre procédure de fenêtre, ainsi la procédure de fenêtre (la procédure qui gère l'affichage et le comportement de votre fenêtre) pourra repeindre le secteur client quand le message WM_PAINT arrivera.
Un autre concept que vous devez prendre en compte est une petite zone rectangle. Windows définit un rectangle invalide comme le plus petit secteur rectangulaire dans le secteur client qui a besoin d'être repeint (rafraîchi) . Quand Windows détecte un rectangle invalide dans le secteur client d'une fenêtre, il envoie le message WM_PAINT à cette fenêtre. En réponse à ce message WM_PAINT, la fenêtre peut obtenir une structure 'Paintstruct' qui contient, entre autre, les coordonnées du rectangle invalide. Vous appelez 'BeginPaint' en réponse au message WM_PAINT pour valider le rectangle invalide. Si vous ne traitez pas le message WM_PAINT, alors vous devez appeler DefWindowProc ou ValidateRect pour valider le rectangle invalide sinon à plusieurs reprises Windows vous enverra le message WM_PAINT. Laissez tombé ça, c'est trop confus.
Voici ci-dessous les étapes que vous devez exécuter en réponse à un message WM_PAINT :
Obtenez l' 'handle du contexte de dispositif' avec BeginPaint.
Repeignez le secteur client (la zone de travail de votre fenêtre).
Libérez l' 'handle du contexte de dispositif' avec EndPaint.
Notez que vous ne devez pas explicitement valider le rectangle invalide. C'est le Call BeginPaint qui s'en occupe automatiquement. Entre la paire de BeginPaint-Endpaint, vous pouvez appeler n'importe quelles fonctions du GDI pour peindre votre secteur client. Presque tous exigent l' 'handle du contexte de dispositif' en tant que paramètre.
Contenu:
Nous allons écrire un programme qui fait apparaître le texte " Win32 assembly is great and easy!" au le centre du secteur client.
.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
mov CommandLine,eax
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
Analyse:
Le code dans sa grande majorité est le même que celui de l'exemple du Tutorial 3. J'expliquerai donc seulement les changements importants.
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
Voici les variables locales qui sont employées par des fonctions du GDI dans notre section WM_PAINT. 'Hdc' est employé pour stocker l'handle du contexte de dispositif renvoyé après l'appel de la fonction BeginPaint. 'ps' est une structure de PAINTSTRUCT. Normalement ce n'est pas vous qui donnez une valeur à ps. On le passe à la fonction BeginPaint et Windows lui donne la valeur appropriée. De la même façon, vous passez 'ps' à la fonction EndPaint quand vous finissez de repeindre le secteur client. 'rect' est une structure rect définie comme suit :
RECT
Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT
ends
Left et top sont les coordonnées du coin supérieur gauche d'un rectangle Right and bottom sont les coordonnées du coin inférieur droit. Une chose à se souvenir : l'origine des axes x-y est au coin supérieur gauche du secteur client. Donc le point y=10 est AU-DESSOUS du point 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 réponse au message WM_PAINT, vous appelez BeginPaint avec la structure PAINTSTRUCT non initialisée et l'handle de la fenêtre que vous souhaitez peindre, comme paramètres. Après un appel couronné de succès, eax contient l'Handle du contexte de dispositif. Ensuite vous appelez GetClientRect pour retrouver la dimension du secteur client. La dimension est renvoyée dans la variable rect que vous passez à DrawText comme un de ses paramètres. La syntaxe de DrawText est :
DrawText
proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD
DrawText est une production de texte de haut niveau créé par une fonction API. Il manipule quelques renseignements peu coutumiers tels que le 'word wrap' (l'enveloppe autour des mots), ou le 'centering' (l'allignement au centre) etc… que vous pourriez appliquez sur le texte que vous souhaitez peindre. Son équivalent de bas niveau, TextOut, sera examiné dans le Tutorial suivant. DrawText définit un texte pour qu'il loge dans les limites d'un rectangle. Il emploie la fonte (la taille, la police) actuellement choisie, la couleur et le fond (dans le contexte de dispositif) pour dessiner le texte. Les lignes sont encadrées pour rester dans les limites du rectangle. Il rend la hauteur du texte produit dans les unités du dispositif, qui dans notre cas sont en pixels. Regardons de plus près ces paramètres :
hdc
est l'handle du contexte de dispositif
lpString
est le pointer de la chaîne de caractères que vous voulez dessiner dans le rectangle. Cette chaîne de caractères doit être terminée par un NULL (0) sinon avec l'autre méthode vous devez spécifier sa longueur dans le paramètre suivant, nCount.
nCount
est le nombre de caractères à afficher. Si la chaîne de caractères est terminée par un NULL (cas précédent), nCount doit être mis à -1. Autrement nCount doit contenir le nombre de caractères de la chaîne que vous voulez dessiner.
lpRect
est le pointer du rectangle (de l'objet de type RECT) que vous voulez dessiner contenant la chaîne de caractères. Notez que ce rectangle est aussi un rectangle de coupure, c'est-à-dire que vous ne pouvez pas dessiner le texte à l'extérieur de ce rectangle.
uFormat
est la valeur qui indique comment la chaîne de caractère est insérée dans le rectangle. Nous employons trois valeurs combinées par l'opérateur "or" :
-
DT_SINGLELINE
indique une simple (une unique)ligne de texte.
-
DT_CENTER
centre le texte horizontallement.
-
DT_VCENTER
centre le texte verticalement. Il doit être employé avec DT_SINGLELINE.
Après que vous ayez fini de repeindre le secteur client, vous devez appeler la fonction EndPaint pour libérer l'handle du contexte de dispositif.
Voilà. Nous pouvons récapituler les points importants:
-
On appelle la paire BeginPaint - EndPaint en réponse au message WM_PAINT.
-
Faites tout ce que vous voulez dans le secteur client entre les appels BeginPaint et EndPaint.
-
Si vous voulez repeindre votre secteur client en réponse à d'autres messages, vous avez deux choix :
(Si il y a une communication entre l'ordi et vous et que vous n'êtes pas en mode texte mais en mode Painting, vous avez deux choix)
-
Employez la paire GetDC - ReleaseDC et faîtes votre 'painting' entre ces 2 appels.
-
Appelez InvalidateRect ou UpdateWindow; pour préserver le secteur client entier de toute écriture, forçant ainsi Windows à renvoyer le message WM_PAINT dans la file d'attente de message de votre fenêtre et faites votre peinture dans la section WM_PAINT.
[Iczelion's
Win32 Assembly HomePage]
Traduit par Morgatte