Un 'tooltip' [una sugerencia] es una pequeña ventana rectangular que se despliega cuando el ratón pasa sobre algún área específica. Una ventana 'tooltip' contiene algún texto que el programador quiere que sea desplegado. En este aspecto, un 'tooltip' tiene el mismo rol que una ventana de estado pero desaparece cuando el usuario mueve el ratón o hace click lejos del área diseñada. Probablemente estarás familiarizado con los 'tooltips' asociados a los botones de barras de herramientas. Esos 'tooltips' son funcionalidades convenientes suministradas por las barras de herramientas. Si quieres 'tooltips' para otras ventanas/controles, necesitas crear tu propio control 'tooltip'.
Ahora que sabes qué es un 'tooltip', veamos cómo podemos crearlo y usarlo. Los pasos están esbozados aquí:
.data
TooltipClassName db "Tooltips_class32",0
.code
.....
invoke InitCommonControls
invoke CreateWindowEx, NULL, addr TooltipClassName, NULL, TIS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL
Nota el estilo de ventana: TIS_ALWAYSTIP. Este estilo especifica que el 'tooltip' será mostrado cuando el puntero del ratón esté sobre el área designada independientemente del esrtdo de la ventana que contiene el área. Para ponerlo con mayor simplicidad, si usas esta bandera, cuando el puntero del ratón pase sobre el área que registraste para el control 'tooltip', la ventana 'tooltip' aparecerá incluso si la ventana debajo del puntero del ratón está activa.
No tienes que incluir los estilos WS_POPUP y WS_EX_TOOLWINDOW en CreateWindowEx porque el procedimiento de ventana del control 'tooltip' la agrega automáticamente. Tampoco necesitas especificar, la altura y el ancho de la ventana del control 'tooltip': el control 'tooltip' los ajustará automáticamente para fijar el texto del 'tooltip' que será desplegado, así que suministramos CW_USEDEFAULT en todos los cuatro parámetros. Los restantes parámetros no son tan relevantes.
El control 'tooltip' es creado pero no mostrado de inmediato. Queremos que la ventana 'tooltip' se muestre cuando el puntero del ratón pase sobre algún área. Ahora es el momento de especificar ese área. Llamamos a tal área "herramienta" [tool]. Una herramienta es un área rectangular sobre el área cliente de una ventana que el control tooltip monitoreará para el puntero del ratón. Si el puntero del ratón pasa sobre la herramienta, aparecerá la ventana 'tooltip'. El área rectangular puede cubrir todo el área cliente o sólo parte de ella. Así que podemos dividir la herramienta en dos tipos: una implementada como una ventana y otra implementada como un área rectangular en el área cliente de alguna ventana. Ambas tienen sus usos. La herramienta que cubre todo el área cliente de una ventana es usada con mucha frecuencia con controles tales como botones, controles de edición, etc. No necesirtas especificar la coordenada y las dimensiones de la herramienta: se asume que estará sobre todo el área cliente de la ventana. La herramienta que es implementada como un área rectangular sobre el área cliente es útil cuando quieres dividir el área cliente de una ventana en varias regiones sin usar ventanas hijas. Con este tipo de herramienta, necesitas especificar la coordenada de la esquina superior izquierda y el ancho y la altura de la herramienta. Con este tipo de herramienta, no necesitas especificar la esquina superior izquierda y el ancho y la altura de la herramienta.
Especificas la herramienta con la estructura TOOLINFO que tiene la siguiente definición:
TOOLINFO STRUCT
cbSize DWORD ?
uFlags DWORD ?
hWnd DWORD ?
uId DWORD ?
rect RECT <>
hInst DWORD ?
lpszText DWORD ?
lParam LPARAM ?
TOOLINFO ENDS
Nombre del Campo | Explicación |
cbSize | El tamaño de la estructura TOOLINFO. DEBES llenar este miembro. Windows no monitoreará el error si este campo no es llenado debidamente y recibirás extraños e impredecibles resultados. |
uFlags | Los bits de bandera que especifican . Este valor
puede ser una combinación de las siguientes banderas:
|
hWnd | Manejador a la ventana que contiene la herramienrta.
Si especificas la bendera TTF_IDISHWND,
este campo es ignorado ya que Windows usará el valor en el miembro
uId como manejador de ventana. Necesitas
llenar este campo si:
|
uId | El valor en este campo puede tener dos significados,
dependiendo si el miembro uFlags contiene
la bandera TTF_IDISHWND.
|
rect | Una estructura RECT que especifica la dimensión de la herramienta. Esta estructura define un rectángulo relativo a la esquina izquierda superior del área cliente de la ventana especificada dpor el miembro hWnd. En pocas palabras, debes llenar esta estructura si quieres especificar una herramienta que cubra sólo una parte del área cliente. El control 'tooltip' ignorará este miembro si especificas la bandera TTF_IDISHWND (eliges usar una herramienta que cubra todo el área cliente) |
hInst | El manejador de la instancia que contiene el recurso de cadena que será usado como texto de sugerencia [tooltip text] si el valor en el miembro lpszText especifica el identificador del recurso de cadena. Esto puede sonar confuso. Lee primero la explicación del miembro lpszText y entenderás para qué es usado este campo. El control 'tooltip' ignorará este campo si el campo lpszText no contiene un identificador del recursos de cadena. |
lpszText | Este campo puede tener diversos valores:
|
Para recapitular, necesitas llenar la estructura TOOLINFO antes de subsumirla al control 'tooltip'. Esta estructura describe las características de la herramienta que deseas.
.data?SendMessage para este mensaje regresará TRUE si la herramienta es satisfactoriamente registrada con el control 'tooltip', FALSE si ese no es el caso.
ti TOOLINFO <>
.......
.code
.......
<fill the TOOLINFO structure>
.......
invoke SendMessage, hwndTooltip, TTM_ADDTOOL, NULL, addr ti
Cuando el paso de arriba es completado, el control 'tooltip' sabe qué área debería monitorearse para los mensajes del ratón y qué texto debería desplegar el conrtrol tooltip. Lo único que falta es el *gatillo* [trigger] para la acción. Piensa en ello: el área especificada por la herramienta está sobre el área cliente de la otra ventana. ¿Cómo puede interceptar el control 'tooltip' los mensajes del ratón para esa ventana? Necesita hacerlo así con el fin de que pueda medir la cantidad de tiempo que el puntero del ratón se halla sobre un punto en la herramienta de manera que cuando caduque el lapso de tiempo especificado, el control 'tooltip' muestre la ventana con la sugerencia [tooltip window]. Hay dos métodos para alcanzar esta meta, una requiere la cooperación de la ventana que contiene la herramienta y la otra sin la cooperación sobre la parte de esa ventana.
WndProc proc hWnd:DWORD, uMsg:DWORD,
wParam:DWORD, lParam:DWORD
.......
if uMsg==WM_CREATE
.............
elseif
uMsg==WM_LBUTTONDOWN || uMsg==WM_MOUSEMOVE ||
uMsg==WM_LBUTTONUP || uMsg==WM_RBUTTONDOWN || uMsg==WM_MBUTTONDOWN || uMsg==WM_RBUTTONUP
|| uMsg==WM_MBUTTONUP
invoke SendMessage, hwndTooltip, TTM_RELAYEVENT, NULL, addr msg
..........
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
EnumChild proto :DWORD,:DWORD
SetDlgToolArea proto :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
.const
IDD_MAINDIALOG equ 101
.data
ToolTipsClassName db "Tooltips_class32",0
MainDialogText1 db "This is the upper left area of the dialog",0
MainDialogText2 db "This is the upper right area of the dialog",0
MainDialogText3 db "This is the lower left area of the dialog",0
MainDialogText4 db "This is the lower right area of the dialog",0
.data?
hwndTool dd ?
hInstance dd ?
.code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_MAINDIALOG,NULL,addr DlgProc,NULL
invoke ExitProcess,eaxDlgProc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL ti:TOOLINFO
LOCAL id:DWORD
LOCAL rect:RECT
.if uMsg==WM_INITDIALOG
invoke InitCommonControls
invoke CreateWindowEx,NULL,ADDR ToolTipsClassName,NULL,\
TTS_ALWAYSTIP,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInstance,NULL
mov hwndTool,eax
mov id,0
mov ti.cbSize,sizeof TOOLINFO
mov ti.uFlags,TTF_SUBCLASS
push hDlg
pop ti.hWnd
invoke GetWindowRect,hDlg,addr rect
invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText1,id,addr rect
inc id
invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText2,id,addr rect
inc id
invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText3,id,addr rect
inc id
invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText4,id,addr rect
invoke EnumChildWindows,hDlg,addr EnumChild,addr ti
.elseif uMsg==WM_CLOSE
invoke EndDialog,hDlg,NULL
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endpEnumChild proc uses edi hwndChild:DWORD,lParam:DWORD
LOCAL buffer[256]:BYTE
mov edi,lParam
assume edi:ptr TOOLINFO
push hwndChild
pop [edi].uId
or [edi].uFlags,TTF_IDISHWND
invoke GetWindowText,hwndChild,addr buffer,255
lea eax,buffer
mov [edi].lpszText,eax
invoke SendMessage,hwndTool,TTM_ADDTOOL,NULL,edi
assume edi:nothing
ret
EnumChild endpSetDlgToolArea proc uses edi esi hDlg:DWORD,lpti:DWORD,lpText:DWORD,id:DWORD,lprect:DWORD
mov edi,lpti
mov esi,lprect
assume esi:ptr RECT
assume edi:ptr TOOLINFO
.if id==0
mov [edi].rect.left,0
mov [edi].rect.top,0
mov eax,[esi].right
sub eax,[esi].left
shr eax,1
mov [edi].rect.right,eax
mov eax,[esi].bottom
sub eax,[esi].top
shr eax,1
mov [edi].rect.bottom,eax
.elseif id==1
mov eax,[esi].right
sub eax,[esi].left
shr eax,1
inc eax
mov [edi].rect.left,eax
mov [edi].rect.top,0
mov eax,[esi].right
sub eax,[esi].left
mov [edi].rect.right,eax
mov eax,[esi].bottom
sub eax,[esi].top
mov [edi].rect.bottom,eax
.elseif id==2
mov [edi].rect.left,0
mov eax,[esi].bottom
sub eax,[esi].top
shr eax,1
inc eax
mov [edi].rect.top,eax
mov eax,[esi].right
sub eax,[esi].left
shr eax,1
mov [edi].rect.right,eax
mov eax,[esi].bottom
sub eax,[esi].top
mov [edi].rect.bottom,eax
.else
mov eax,[esi].right
sub eax,[esi].left
shr eax,1
inc eax
mov [edi].rect.left,eax
mov eax,[esi].bottom
sub eax,[esi].top
shr eax,1
inc eax
mov [edi].rect.top,eax
mov eax,[esi].right
sub eax,[esi].left
mov [edi].rect.right,eax
mov eax,[esi].bottom
sub eax,[esi].top
mov [edi].rect.bottom,eax
.endif
push lpText
pop [edi].lpszText
invoke SendMessage,hwndTool,TTM_ADDTOOL,NULL,lpti
assume edi:nothing
assume esi:nothing
ret
SetDlgToolArea endp
end start
invoke InitCommonControlsDespués de eso procedemos a definir cuatro herramientas para cada esquina de la caja de diálogo.
invoke CreateWindowEx,NULL,ADDR ToolTipsClassName,NULL,\
TTS_ALWAYSTIP,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInstance,NULL
mov hwndTool,eax
mov id,0
; usado como el ID de la herramienta
mov ti.cbSize,sizeof TOOLINFO
mov ti.uFlags,TTF_SUBCLASS ;
dice al control tooltip que subclasifique la ventana del diálogo.
push hDlg
pop ti.hWnd ;
manejador para la ventana que contiene la herramienta
invoke GetWindowRect,hDlg,addr rect;
obtener la dimensión del área cliente
invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText1,id,addr
rect
Inicializamos los miembros de la estructura TOOLINFO. Nota que queremos dividir el área cliente en 4 herramientas, así que necesitamos saber la dimensión del área cliente. Esa es la razón por la cual llamamos a GetWindowRect. No queremos transmitir mensajes de ratón al control 'control' nosotros mismos, así que especificamos la bandera TIF_SUBCLASS.
SetDlgToolArea es una función que calcula el rectángulo asociado de cada herramienta y registra la herramienta para el control 'tooltip'. No entraré en detalles engorrosos sobre el cálculo, es suficiente decir que divide el área cliente en 4 áreas con los mismos tamaños. Luego envía el mensaje TTM_ADDTOOL al control 'tooltip', pasando la dirección de la estructura TOOLINFO en el parámetro lParam.
invoke SendMessage,hwndTool,TTM_ADDTOOL,NULL,lpti
Después de que son registradas las 4 herramientas, podemos ocuparnos en los botones de la caja de diálogo. Podemos manejar cada botón a través de su ID pero esto es muy tedioso.En vez de eso usaremos la llamada a la API EnumChildWindows para enumerar todos los controles en la caja de diálogo y luego registrarlos para el control 'tooltip'. EnumChildWindows tiene la siguiente sintaxis:
EnumChildWindows proto hWnd:DWORD, lpEnumFunc:DWORD, lParam:DWORDhWnd es el manejador de la ventana padre. lpEnumFunc es la dirección de la función EnumChildProc que será llamada para cada control enumerado. lParam es el valor definido de la aplicación [application-defined value] que será pasado a la función EnumChildProc. La a función EnumChildProc tiene la siguiente definición:
EnumChildProc proto hwndChild:DWORD, lParam:DWORDhwndChild es el manejador a un control enumerado por EnumChildWindows. lParam es el mismo valor lParam que pasas a 0 EnumChildWindows.
invoke EnumChildWindows,hDlg,addr EnumChild,addr ti
Pasamos la dirección de la estructura TOOLINFO en el parámetro lParam porque registraremos cada control de ventana hija para el control 'tooltip' en la función EnumChild. Si no queremos usar este método, necesitaremos declarar ti como una variable global que puede introducir errores [bugs].
Cuando llamamos a EnumChildWindows, Windows enumerará los controles de ventanas hijas sobre nuestra caja de diálogo y llamamos a la función EnumChild una vez para cada control enumerado. De esta manera, si nuestra caja de diálogo tiene dos controles, EnumChild será llamada dos veces.
La función EnumChild llena los miembros relevantes de la estructura TOOLINFO y luego registra la herramienta con el control 'tooltip'.
EnumChild proc uses edi hwndChild:DWORD,lParam:DWORDNota que en este caso, un tipo diferente de herramienta: una que cubre todo el área cliente de la ventana. Así que necesitamos llenar todo el campo uID con el manejador de ventana que contiene la herramienta. También debemos especificar la benadera TTF_IDISHWND en el miembrio uFlags.
LOCAL buffer[256]:BYTE
mov edi,lParam
assume edi:ptr TOOLINFO
push hwndChild
pop [edi].uId ; usamos todo el área cliente del control como herramienta
or [edi].uFlags,TTF_IDISHWND
invoke GetWindowText,hwndChild,addr buffer,255
lea eax,buffer ; usar el texto de la ventana como texto de sugerencia
mov [edi].lpszText,eax
invoke SendMessage,hwndTool,TTM_ADDTOOL,NULL,edi
assume edi:nothing
ret
EnumChild endp
[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