Ассемблер для Windows


В данном разделе мы рассмотрим весьма интересный вопрос о всплывающих подсказках



III

В данном разделе мы рассмотрим весьма интересный вопрос о всплывающих подсказках. В визуальных языках программирования всплывающие подсказки организуются посредством установки соответствующих свойств объектов, расположенных на объекте-контейнере. Наша с вами задача разработать механизм, позволяющий без каких-либо дополнительных библиотек устанавливать подсказки на любые объекты, расположенные в окне. Итак, приступаем.

1. Прежде всего заметим, что всплывающая подсказка - это всего лишь окно с определенными свойствами. Вот эти свойства: DS_3DLOOK, WS_POPUP, WS_VISIBLE, WS_BORDER. В принципе можно экспериментировать - добавлять или удалять свойства. Но без одного свойства Вы никак не обойдетесь - это WS_POPUP. Собственно POPUP можно перевести как поплавок. Кроме того, определение всплывающего окна в файле ресурсов не должно содержать опции CAPTION.

2. Появление подсказки не должно менять ситуацию в диалоговом окне. Это значит - вызов подсказки должен быть немодальным, при помощи функции CreateDialogIndirect. Кроме того, следует предусмотреть переустановку фокуса на диалоговое окно. Для этого достаточно в нужном месте (см. Рисунок 3.1.3) вызвать функцию SetFocus.

3. Итак, подсказка - это диалоговое окно, и, следовательно, оно должно иметь свою функцию. Что должна содержать эта функция? По крайней мере, обработку трех событий: WM_INITDIALOG, WM_PA1NT, WM_TIMER. По получении сообщения WM_INITDIALOG следует определить размер и положение подсказки. Кроме того, если мы предполагаем, что подсказка должна спустя некоторое время исчезать, следует установить таймер. По получении сообщения WM_PAINT следует вывести в окно подсказки текст. Если определять размер окна подсказки точно по строке выводимого текста, то цвет фона подсказки будет полностью определяться цветом выводимого текста. Наконец по приходе сообщения WM_TIMER мы закрываем подсказку.

4. С самой подсказкой более или менее ясно. Определимся теперь, как и где будет вызываться эта подсказка. Мне более импонирует такой подход: в основном диалоговом окне определяем таймер, в функции которого и будет проверяться положение курсора. В зависимости от этого положения и будет вызываться или удаляться подсказка. В функции таймера должно предусмотреть:






  1. Проверку положения курсора. Если курсор оказался на данном элементе, то вызывать подсказку. При этом желательно, чтобы подсказка появлялась бы с некоторой задержкой. Последнее можно обеспечить введением счетчика - вызывать подсказку, если счетчик превысил некоторое значение.
  2. Необходимо обеспечить удаление подсказки, если курсор покидает данный элемент.


  3. На Рисунок 3.1.3 представлена программа, которая демонстрирует описанный выше подход. На Рисунок 3.1.4 представлено диалоговое окно с подсказками. В принципе, означенный подход не является единственным, и, разобравшись в данном подходе, вы сможете пофантазировать и придумать свои способы создания подсказок.

    // файл HINT.RC // определение констант #define WS_SYSMENU 0x00080000L // элементы на окне должны быть изначально видимы #define WS_VISIBLE 0x10000000L // бордюр вокруг элемента #define WS_BORDER 0x00800000L // при помощи TAB можно по очереди активизировать элементы #define WS_TABSTOP 0x00010000L // текст в окне редактирования прижат к левому краю #define ES_LEFT 0x0000L // стиль всех элементов на окне #define WS_CHILD 0x40000000L // стиль - кнопка #define BS_PUSHBUTTON 0x00000000L // центрировать текст на кнопке #define BS_CENTER 0x00000300L // тип окна - "поплавок" #define WS_POPUP 0x80000000L // стиль - диалоговое окно Windows 95 #define DS_3DLOOK 0x0004L

    // определение диалогового окна DIAL1 DIALOG 0, 0, 240, 100 STYLE WS_SYSMENU | DS_3DLOOK CAPTION "Окно с всплывающими подсказками" FONT 8, "Arial" { // окно редактирования, идентификатор 1 CONTROL "", 1, "edit", ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP , 100, 5, 130, 12 // кнопка, идентификатор 2 CONTROL "Выход", 2, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 180, 76, 50, 14 }



    // диалоговое окно подсказки HINTW DIALOG 0, 0, 240, 8 STYLE DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_BORDER FONT 8, "MS Sans Serif" { }

    ; файл HINT.INC ;константы ;цвет фона окна подсказки RED = 255 GREEN = 255 BLUE = 150 RGBB equ (RED or (GREEN shl 8)) or (BLUE shl 16)



    ; цвет текста окна подсказки RED = 20 GREEN = 20 BLUE = 20 RGBT equ (RED or (GREEN shl 8)) or (BLUE shl 16)

    ;сообщение приходит при закрытии окна 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

    ;прототипы внешних процедур 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@20 = CreateDialogParamA SetFocus@4 = SetFocus lstrcpyA@8 = IstrcpyA DestroyWindow@4 = DestroyWindow lstrlenA@4 = IstrlenA 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



    ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS

    ; структура размера окна RECT STRUC L DD ? T DD ? R DD ? B DD ? RECT ENDS

    ;структура размер SIZ STRUC X DD ? Y DD ? SIZ ENDS

    ; структура для 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

    ; структура для получения позиции курсора POINT STRUC X DD ? Y DD ? POINT ENDS

    ; файл HINT.ASM .386P ; плоская модель .MODEL FLAT, stdcall include hint.inc ; директивы компоновщику для подключения библиотек IFDEF MASM ; для компоновщика LINK.EXE includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ELSE ; для компоновщика TLINK32.EXE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ; дескриптор приложения PA DB "DIAL1",0 HIN DB "HINTW",0 XX DD ? YY DD ? ;------------------------------- R1 RECT <?> R2 RECT <?> S SIZ <?> PS PAINTSTR <?> PT POINT <?> ; дескрипторы окон-подсказок, для первого и второго элемента H1 DD 0 H2 DD 0 ; строка-подсказка HINTS DB 60 DUP (?) ; перечень подсказок HINT1 DB "Редактирование строки",0 HINT2 DB "Кнопка выхода",0 ; для временного хранения контекста устройства DC DD ? ; счетчик P1 DD ? _DATA ENDS

    ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения 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 ;-----------------------------

    ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10Н] ; 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 ; здесь реакция на закрытие окна ; удалить таймер L4: PUSH 2 ; идентификатор таймера PUSH DWORD PTR [EBP+08H] CALL KillTimer@8 ; закрыть диалог PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH], WM_INITDIALOG JNE L2 ; здесь начальная инициализация ; установить таймер PUSH OFFSET TIMPROC PUSH 500 ; интервал 0.5 с. PUSH 2 ; идентификатор таймера PUSH DWORD PTR [EBP+08H] CALL SetTimer@16 JMP FINISH L2: CMP DWORD PTR [EBP+0CH],WM_COMMAND JNE L3 ; кнопка выхода? 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



    ;--------------------------------------------- ; процедура таймера ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM - промежуток запуска Windows ; [EBP+10Н] ; WAPARAM - идентификатор таймера ; [EBP+0CH] ; WM_TIMER ; [EBP+8] ; HWND TIMPROC PROC PUSH EBP MOV EBP,ESP ; получить положение курсора PUSH OFFSET PT CALL GetCursorPos@4 ; запомнить координаты MOV EAX,PT.X MOV XX,EAX MOV EAX,PT.Y MOV YY,EAX ; получить положение элементов ; окно редактирования PUSH 1 PUSH DWORD PTR [EBP+08H] CALL GetDlgItem@8 PUSH OFFSET R1 PUSH EAX CALL GetWindowRect@8 ; кнопка выхода PUSH 2 PUSH DWORD PTR [EBP+08H] CALL GetDlgItem@8 PUSH OFFSET R2 PUSH EAX CALL GetWindowRect@8 ; увеличить счетчик INC P1 MOV ECX,XX MOV EDX,YY ; проверка условий .IF H1==0 && P1>5 .IF EDX<=R1.B && EDX>=R1.T && ECX>=R1.L && ECX<=R1.R ; подготовить строку PUSH OFFSET HINT1 PUSH OFFSET HINTS CALL lstrcpyA@8 ; создать диалоговое окно - подсказку PUSH 0 PUSH OFFSET HINT PUSH DWORD PTR [EBP+08H] PUSH OFFSET HIN PUSH [HINST] CALL CreateDialogParamA@20 MOV H1,EAX ; установить фокус PUSH DWORD PTR [EBP+08H] CALL SetFocus@4 ; обнулить счетчик MOV P1,0 JMP _END .ENDIF .ENDIF .IF H1!=0 .IF (EDX>R1.B || EDX<R1.T) || (ECX<R1.L || ECX>R1.R) ; удаление подсказки в связи с перемещением курсора 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 ; подготовить строку PUSH OFFSET HINT2 PUSH OFFSET HINTS CALL lstrcpyA@8 ; создать диалоговое окно - подсказку PUSH 0 PUSH OFFSET HINT PUSH DWORD PTR [EBP+08H] PUSH OFFSET HIN PUSH [HINST] CALL CreateDialogParamA@20 MOV H2,EAX ; установить фокус PUSH DWORD PTR [EBP+08H] CALL SetFocus@4 ; обнулить счетчик MOV P1,0 JMP _END .ENDIF .ENDIF .IF H2!=0 .IF (EDX>R2.B || EDX<R2.T) || (ECX<R2.L || ECX>R2.R) ;удаление подсказки в связи с перемещением курсора PUSH H2 CALL DestroyWindow@4 MOV H2,0 JMP _END .ENDIF .ENDIF ; восстановить стек _END: POP EBP RET 16 TIMPROC ENDP



    ; процедура окна всплывающей подсказки HINT PROC PUSH EBP MOV EBP,ESP CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE NO_INIT ; инициализация ; получить контекст PUSH DWORD PTR [EBP+08H] CALL GetDC@4 MOV DC,EAX ; получить длину строки PUSH OFFSET HINTS CALL lstrlenA@4 ; получить длину и ширину строки PUSH OFFSET S PUSH EAX PUSH OFFSET HINTS PUSH DC CALL GetTextExtentPoint32A@16 ; установить положение и размер окна-подсказки 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 ; закрыть контекст PUSH DC PUSH DWORD PTR [EBP+08H] CALL ReleaseDC@8 ; установить таймер PUSH 0 PUSH 6000 ; интервал 6 с. PUSH 3 ; идентификатор таймера PUSH DWORD PTR [EBP+08H] CALL SetTimer@16 JMP FIN NO_INIT: CMP DWORD PTR [EBP+0CH],WM_PAINT JNE NO_PAINT ; перерисовка окна ; получить контекст PUSH OFFSET PS PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 MOV DC,EAX ; установить цвета фона и текста подсказки PUSH RGBB PUSH EAX CALL SetBkColor@8 PUSH RGBT PUSH DC CALL SetTextColor@8 ; вывести текст PUSH OFFSET HINTS CALL lstrlenA@4 PUSH EAX PUSH OFFSET HINTS PUSH 0 PUSH 0 PUSH DC CALL TextOutA@20 ; закрыть контекст PUSH OFFSET PS PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 JMP FIN NO_PAINT: CMP DWORD PTR [EBP+0CH],WM_TIMER JNE FIN ; обработка события таймера ; удалить таймер и удалить диалоговое окно ; подсказка удаляется в связи с истечением срока 6 с. 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


    Содержание раздела