| ||
In this section, the interesting topic of popup help windows is considered . In visual programming languages, popup help windows are organized by setting appropriate properties for objects located within a container object. Your goal is to develop the mechanism that would allow you to set popup help windows for any window control without using additional libraries. To achieve this, it is necessary to proceed as follows :
First, it is necessary to notice that a popup help window is simply a window with predefined properties. Here are those properties: DS_3DLOOK , WS_POPUP , WS_VISIBLE , and WS_BORDER . You can experiment by adding or removing the properties. However, there is one property, without which your goal cannot be achieved. This is the WS_POPUP property. Furthermore, the definition of the popup window in the resource file must not contain the CAPTION property.
The display of the popup help window must not change the situation in the dialog. This means that the popup help call must be modeless by calling the CreateDialogIndirect function. Moreover, it is necessary to make provision for resetting the focus to the dialog box. For this purpose, it is enough to call the SetFocus function when needed (see Listing 14.3).
Thus, the popup help window is the dialog; consequently, it must have the window function. What should be contained in this function? At the least, it must process the following three events: WM_INITDIALOG , WM_PAINT , and WM_TIMER . Having received the WM_INITDIALOG message, it is necessary to determine the size and position of the popup window. Furthermore, if you assume that the popup window must automatically close after a certain time interval elapses, then it is necessary to set the timer. When the WM_PAINT message is received, it is necessary to output the text into the popup window. If the size of the popup window is set exactly according to the length of the text string that has to be displayed, then the popup window background color will be fully defined by the color of the displayed text. Finally, when the WM_TIMER message arrives, it is necessary to close the popup window.
As relates to the help string, everything is more or less clear. Now, you must determine where and how this help will be called. The following approach seems preferable to me: In the main dialog, determine the timer whose function would trace the cursor position. Depending on this position, the popup help window will be either called or closed. The timer function must make provision for the following:
It must check the cursor position. If the cursor happens to be over the required element, then it is necessary to call the help. At the same time, it is desirable to make sure that popup windows appear with a certain delay. This can be ensured by introducing a counter call popup help if the counter exceeds a predefined value.
When the cursor moves from the area of the required element, the popup help window must be closed.
Listing 14.3 provides a program that demonstrates the described approach. Fig. 14.2 shows a dialog with popup help. Principally, this approach is not the only possible one. After you understand the working principle of this program, you'll be able to invent your own methods of creating popup help windows.
// The HINT.RC file // Constant definitions #define WS_SYSMENU 0x00080000L // Window elements must be initially visible #define WS_VISIBLE 0x10000000L // Border around the element #define WS_BORDER 0x00800000L // Elements can be activated using the <TaB> key #define WS_TABSTOP 0x00010000L // Text in the help window is left-aligned #define ES_LEFT 0x0000L // Type of all elements of the window #define WS_CHILD 0x40000000L // Style - Button #define BS_PUSHBUTTON 0x00000000L // Center the button label #define BS_CENTER 0x00000300L // Window type---Popup #define WS_POPUP 0x80000000L // Windows 95-style dialog #define DS_3DLOOK 0x0004L // Dialog box definition DIAL1 DIALOG 0, 0, 240, 100 STYLE WS_SYSMENU DS_3DLOOK CAPTION "Window with popup help" FONT 8, "Arial" { // Edit window, identifier 1 CONTROL "", 1, "edit", ES_LEFT WS_CHILD WS_VISIBLE WS_BORDER WS_TABSTOP , 100, 5, 130, 12 // Button, identifier 2 CONTROL "Exit", 2, "button", BS_PUSHBUTTON BS_CENTER WS_CHILD WS_VISIBLE WS_TABSTOP, 180, 76, 50, 14 } // Help dialog HINTW DIALOG 0, 0, 240, 8 STYLE DS_3DLOOK WS_POPUP WS_VISIBLE WS_BORDER FONT 8, "MS Sans Serif" { } ; The HINT.INC file ; Constants ; Help window background color RED = 255 GREEN = 255 BLUE = 150 RGBB equ (RED or (GREEN shl 8)) or (BLUE shl 16) ; Foreground color of the popup help window RED = 20 GREEN = 20 BLUE = 20 RGBT equ (RED or (GREEN shl 8)) or (BLUE shl 16) ; This message arrives when the window is closed WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_COMMAND equ 111h WM_TIMER equ 113h WM_SETTEXT equ 0Ch WM_COMMAND equ 111h WM_PAINT equ 0Fh ; Prototypes of external procedures IFDEF MASM EXTERN CreateDialogParamA@20:NEAR EXTERN SetFocus@4:NEAR EXTERN lstrcpyA@8:NEAR EXTERN DestroyWindow@ 4:NEAR EXTERN lstrlenA@4:NEAR EXTERN GetDlgItem@8:NEAR EXTERN GetCursorPos@4:NEAR EXTERN TextOutA@ 20:NEAR EXTERN SetBkColor@8:NEAR EXTERN SetTextColor@8:NEAR EXTERN BeginPaint@8:NEAR EXTERN EndPaint@8:NEAR EXTERN GetTextExtentPoint32A@16:NEAR - EXTERN MoveWindow@24:NEAR EXTERN GetWindowRect@8:NEAR EXTERN ReleaseDC@8:NEAR EXTERN GetDC@4:NEAR EXTERN SendDlgItemMessageA@20:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN SetTimer@16:NEAR EXTERN KillTimer@8:NEAR ELSE EXTERN CreateDialogParamA:NEAR EXTERN SetFocus:NEAR EXTERN lstrcpyA:NEAR EXTERN DestroyWindow:NEAR EXTERN lstrlenA:NEAR EXTERN GetDlgItem:NEAR EXTERN GetCursorPos:NEAR EXTERN TextOutA:NEAR EXTERN SetBkColor:NEAR EXTERN SetTextColor:NEAR EXTERN BeginPaint:NEAR EXTERN EndPaint:NEAR EXTERN GetTextExtentPoint32A:NEAR EXTERN MoveWindow:NEAR EXTERN GetWindowRect:NEAR EXTERN ReleaseDC:NEAR EXTERN GetDC:NEAR EXTERN SendDlgItemMessageA:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN SetTimer:NEAR EXTERN KillTimer:NEAR CreateDialogParamA@2O = CreateDialogParamA SetFocus@4 = SetFocus lstrcpyA@8 = lstrcpyA DestroyWindow@4 = DestroyWindow lstrlenA@4 = lstrlenA GetDlgItem@8 = GetDlgItem GetCursorPos@4 =GetCursorPos TextOutA@20 = TextOutA SetBkColor@8 = SetBkColor SetTextColor@8 = SetTextColor BeginPaint@8 = BeginPaint EndPaint@8 = EndPaint GetTextExtentPoint32A@16 = GetTextExtentPoint32A MoveWindow@24 = MoveWindow GetWindowRect@8 = GetWindowRect ReleaseDC@8 = ReleaseDC GetDC@4 = GetDC SendDlgItemMessageA@20 = SendDlgItemMessageA ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog SetTimer@16 = SetTimer KillTimer@8 = KillTimer ENDIF ; Structures ; Message structure MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ; Window size stricture RECT STRUC L DD ? T DD ? R DD ? B DD ? RECT ENDS ; Size structure SIZ STRUC X DD ? Y DD ? SIZ ENDS ; Structure for BeginPaint PAINTSTR STRUC hdc DWORD 0 fErase DWORD 0 left DWORD 0 top DWORD 0 right DWORD 0 bottom DWORD 0 fRes DWORD 0 fIncUp DWORD 0 Reserv DB 32 dup(0) PAINTSTR ENDS ; Structure for getting the cursor position POINT STRUC X DD ? Y DD ? POINT ENDS ; The HINT.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall include hint.inc ; INCLUDELIB directives for the linker IFDEF MASM ; For the LINK.EXE linker includelib C:\masm32\lib\user32.lib includelib C:\masm32\lib\kernel32.lib includelib C:\masm32\lib\gdi32.lib ELSE ; For the TLINK32.EXE linker includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- ; Data segment _DATA SEGMENT MSG MSGSTRUCT <?> HINST DD 0 ; Application descriptor PA DB "DIAL1", 0 HIN DB "HINTW", 0 XX DD ? YY DD ? ;-------------------------- R1 RECT <?> R2 RECT <?> S SIZ <?> PS PAINTSTR <?> PT POINT <?> ; Descriptors for popup windows for the first and the second elements H1 DD 0 H2 DD 0 ; Help string HINTS DB 60 DUP(?) ; List of help strings HINT1 DB "Edit string", 0 HINT2 DB "Exit button", 0 ; For temporarily storing the device context DC DD ? ; Counter P1 DD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get the application descriptor PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;-------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX, -1 JNE KOL KOL: ;-------------------------------- PUSH 0 CALL ExitProcess@4 ;-------------------------------- ; Window procedure ; Position of parameters in the stack ; [EBP+014H] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI ;---------------- CMP DWORD PTR [EBP+0CH], WM_CLOSE JNE L1 ; The reaction to closing the window ; Delete the timer L4: PUSH 2 ; Timer identifier PUSH DWORD PTR [EBP+08H] CALL KillTimer@8 ; Close the dialog PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH], WM_INITDIALOG JNE L2 ; Startup initialization ; Set the timer PUSH OFFSET TIMPROC PUSH 500 ; 0.5-second interval PUSH 2 ; Timer identifier PUSH DWORD PTR [EBP+08H] CALL SetTimer@16 JMP FINISH L2: CMP DWORD PTR [EBP+0CH], WM_COMMAND JNE L3 ; Exit button? CMP WORD PTR [EBP+10H], 2 JNE L3 JMP L4 L3: FINISH: POP EDI POP ESI POP EBX POP EBP MOV EAX, 0 RET 16 WNDPROC ENDP ;----------------------------------------------- ; Timer procedure ; Position of parameters in the stack ; [EBP+014H] ; LPARAM - Time elapsed since Windows startup ; [EBP+10H] ; WAPARAM - Timer identifier ; [EBP+0CH] ; WM_TIMER ; [EBP+8] ; HWND TIMPROC PROC PUSH EBP MOV EBP, ESP ; Get the cursor position PUSH OFFSET PT CALL GetCursorPos@4 ; Store the coordinates MOV EAX, PT.X MOV XX, EAX MOV EAX, PT.Y MOV YY, EAX : Get the elements positions : Edit window PUSH 1 PUSH DWORD PTR [EBP+08H] CALL GetDlgItem@8 PUSH OFFSET R1 PUSH EAX CALL GetWindowRect@8 ; Exit button PUSH 2 PUSH DWORD PTR [EBP+08H] CALL GetDlgItem@8 PUSH OFFSET R2 PUSH EAX CALL GetWindowRect@8 ; Increase the counter INC P1 MOV ECX, XX MOV EDX, YY ; Check the conditions .IF H1==0 && P1>5 .IF EDX<=R1.B && EDX>=R1.T && ECX>=R1.L && ECX<=R1.R ; Prepare the string PUSH OFFSET HINT1 PUSH OFFSET HINTS CALL lstrcpyA@8 ; Create the popup dialog PUSH 0 PUSH OFFSET HINT PUSH DWORD PTR [EBP+08H] PUSH OFFSET HIN PUSH [HINST] CALL CreateDialogParamA@20 MOV H1, EAX ; Set the focus PUSH DWORD PTR [EBP+08H] CALL SetFocus@4 ; Reset the counter to zero MOV P1, 0 JMP _END .ENDIF .ENDIF .IF H1!=0 .IF (EDX>R1.B EDX<R1.T) (ECX<R1.L ECX>R1.R) ; Delete the popup window because of the change of the cursor position PUSH H1 CALL DestroyWindow@4 MOV H1, 0 JMP _END .ENDIF .ENDIF .IF H2==0 && P1>5 .IF EDX<=R2.B && EDX>=R2.T && ECX>=R2.L && ECX<=R2.R ; Prepare the string PUSH OFFSET HINT2 PUSH OFFSET HINTS CALL lstrcpyA@8 ; Create the popup dialog PUSH 0 PUSH OFFSET HINT PUSH DWORD PTR [EBP+08H] PUSH OFFSET HIN PUSH [HINST] CALL CreateDialogParamA@20 MOV H2, EAX ; Set the focus PUSH DWORD PTR [EBP+08H] CALL SetFocus@4 ; Reset the counter to zero MOV P1, 0 JMP _END .ENDIF .ENDIF .IF H2!=0 .IF (EDX>R2.B EDX<R2.T) (ECX<R2.L ECX>R2.R) ; Delete the popup window because of the change of the cursor position PUSH H2 CALL DestroyWindow@4 MOV H2, 0 JMP _END .ENDIF .ENDIF ; Restore the stack _END: POP EBP RET 16 TIMPROC ENDP ; Window procedure of popup help HINT PROC PUSH EBP MOV EBP, ESP CMP DWORD PTR [EBP+0CH], WM_INITDIALOG JNE NO_INIT ; Initialization ; Get the context PUSH DWORD PTR [EBP+08H] CALL GetDC@4 MOV DC, EAX ; Get the string length PUSH OFFSET HINTS CALL lstrlenA@4 ; Get the string length and width PUSH OFFSET S PUSH EAX PUSH OFFSET HINTS PUSH DC CALL GetTextExtentPoint32A@16 ; Set the position and the size of the popup window PUSH 0 PUSH S.Y ADD S.X, 2 PUSH S.X SUB YY, 20 PUSH YY ADD XX, 10 PUSH XX PUSH DWORD PTR [EBP+08H] CALL MoveWindow@24 ; Close the context PUSH DC PUSH DWORD PTR [EBP+08H] CALL ReleaseDC@8 ; Set the timer PUSH 0 PUSH 6000 ; 6-second interval PUSH 3 ; Timer identifier PUSH DWORD PTR [EBP+08H] CALL SetTimer@16 JMP FIN NO_INIT: CMP DWORD PTR [EBP+0CH], WM_PAINT JNE NO_PAINT ; Redraw the window ; Get the context PUSH OFFSET PS PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 MOV DC, EAX ; Set the foreground and background colors PUSH RGBB PUSH EAX CALL SetBkColor@8 PUSH RGBT PUSH DC CALL SetTextColor@8 ; Outpur the text PUSH OFFSET HINTS CALL lstrlenA@4 PUSH EAX PUSH OFFSET HINTS PUSH 0 PUSH 0 PUSH DC CALL TextOutA@20 ; Close the context PUSH OFFSET PS PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 JMP FIN NO_PAINT: CMP DWORD PTR [EBP+0CH], WM_TIMER JNE FIN ; Process the timer event ; Delete the timer close the dialog ; The popup help window is closed because the ; 6-second timer has expired PUSH 3 PUSH DWORD PTR [EBP+08H] CALL KillTimer@8 PUSH DWORD PTR [EBP+08H] CALL DestroyWindow@4 FIN: POP EBP RET 16 HINT ENDP _TEXT ENDS END START
The program presented in Listing 14.3 requires some comments.
First, note that this program uses conditional run-time constructs. This approach is logical. It is justified by the needs to reduce the program size and improve its readability. Nesting of the conditional constructs and placement of braces are based on the desire to reduce the string length yet preserve program compatibilitythe possibility of translating it using both MASM32 and TASM32. As I have already mentioned, these two assemblers differ significantly, especially when speaking about macros.
To translate this program, issue the following commands using MASM32:
ML /c /coff /DMASM hint.asm RC hint.rc LINK /SUBSYSTEM:WINDOWS hint.obj hint.res
To translate this program, issue the following commands using TASM32:
TASM32 /ml hint.asm BRCC32 hint.rc TLINK32 -aa hint.obj,,,,,hint.res
As you probably understand, the timer procedure checks the cursor position every 0.5 seconds. If the cursor is positioned over some element (e.g., edit window or button) and popup help has not been called yet ( H1 or H2 is different from zero), then popup help is called. At the same time, the value of the counter ( P1 ) is taken into account to ensure that the popup help window appears with a certain delay. If the procedure is next called when the cursor is positioned outside the element and the popup window still displayed, it will be closed. However, this mechanism does not account for a cursor that quickly moves from one element to another. In this case, there could be two popup help windows on the screen. The first popup must be closed immediately.
In Listing 14.3, the dialog box has only two elements: the edit window and the button. I wanted to note that popup help can be specified for any element. The position of the popup window in relation to the cursor can be easily regulated . You can change it yourself.
The GetCursorPos function places the cursor position in coordinates relative to the screen. No problems will arise here, because the GetWindowsRec function also places the position of the window element in absolute coordinates. Before that, you have to determine the window descriptor using the GetDlgItem function.
| ||