Подсказки (tooltips) в диалоговых окнах
Еще один способ придать вашим программам профессиональный вид - использование всплывающих подсказок (tooltips), которые появляются при наведении курсора мыши на какой-нибудь элемент диалогового окна. Готовые примеры на FASM или слишком громоздкие, или недостаточно гибкие, поэтому пришлось придумывать что-то свое. Причем с самого начала поставил себе задачу сделать функции работы с подсказками как можно более универсальными, чтобы в дальнейшем их можно было легко использовать в других проектах.В сегменте данных определим все необходимые константы, переменные и структуры. Большая часть этих данных в FASM по умолчанию отсутствует, так что пришлось штудировать MSDN и другие источники и описывать их самостоятельно.
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- hwndTip dd ? ; Хэндл окна подсказки
- TTipFlag dd ? ; Флаг активности подсказки
- ; Структура TRACKMOUSEEVENT не определена, сделаем это сами
- struct TRACKMOUSEEVENT
- cbSize dd ?
- dwFlags dd ?
- hwndTrack dd ?
- dwHoverTime dd ?
- ends
- ; Определяем нужные структуры
- tme TRACKMOUSEEVENT
- pt POINT
- ttip TOOLINFO
- trect RECT
- oldX dd ? ; Сохраненные координаты мыши
- oldY dd ?
- ; Определяем нужные константы
- TTM_TRACKACTIVATE = WM_USER + 17
- TTM_TRACKPOSITION = WM_USER + 18
- TTM_SETMAXTIPWIDTH = WM_USER + 24
- ; Сообщения окну подсказки
- TTF_SUBCLASS = 0x0010
- TTF_TRACK = 0x0020
- TTF_ABSOLUTE = 0x0080
- TTF_TRANSPARENT = 0x0100
- ; Сообщение обработчика мыши
- TME_LEAVE = 0x00000002
- ; Максимальная ширина всплывающей подсказки
- TOOLTIP_WIDTH = 200
- ; Предустановленное количество элементов в массиве подсказок
- TOOLTIPS_COUNT = 5
- ; Массив данных для всплывающих подсказок
- ; Формат массива:
- ; 1 DWORD - сохраненный адрес обработчика
- ; 2 DWORD - адрес подсказки
- tt_data rd (TOOLTIPS_COUNT*2)
Параметры вызова: hwnd - хэндл окна, к которому добавляется подсказка, lpText - указатель на строку подсказки в формате ASCIIZ, lpData - указатель на два DWORD (элемент массива), в которых будет храниться адрес предыдущей процедуры обработчика окна и указатель на строку подсказки.
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------------------
- ; Процедура добавления подсказки к окну
- ; Входные параметры:
- ; hwnd - хэндл окна, к которому добавляется подсказка
- ; lpText - указатель на строку подсказки
- ; lpData - указатель на данные (2 DWORD)
- ;----------------------------------------------------------------------
- proc AddTooltip hwnd:DWORD, lpText:DWORD, lpData:DWORD
- pusha
- ; Оно подсказки уже инициализировано?
- cmp [hwndTip],0
- jne @f
- invoke GetModuleHandle,0
- ; Создать окно подсказки
- invoke CreateWindowEx, NULL, ttclass, NULL, TTS_ALWAYSTIP,\
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,\
- NULL, NULL, eax, NULL
- ; Сохранить хэндл окна подсказки
- mov [hwndTip],eax
- ; Определить константу CS_DROPSHADOW
- CS_DROPSHADOW = 00020000h
- ; Получить текущее значение стиля окна
- invoke GetWindowLong,[hwndTip],GCL_STYLE
- ; Добавить к нему атрибут тень
- or eax,CS_DROPSHADOW
- ; Установить новый стиль окна
- invoke SetClassLong,[hwndTip],GCL_STYLE,eax
- ; Заполнение структуры для подсказки
- mov [ttip.cbSize],sizeof.TOOLINFO
- mov [ttip.hwnd],NULL
- mov [ttip.uFlags],TTF_IDISHWND+TTF_TRACK+TTF_ABSOLUTE+TTF_SUBCLASS
- mov [ttip.uId],NULL
- mov [ttip.lpszText],szNull
- ; Создать подсказку
- invoke SendMessage,[hwndTip],TTM_ADDTOOL,0,ttip
- ; Установить максимальную ширину. При этом длинный текст будет
- ; переноситься на следующую строку
- invoke SendMessage,[hwndTip],TTM_SETMAXTIPWIDTH,0,TOOLTIP_WIDTH
- ; Установить подсказку поверх всех окон
- invoke SetWindowPos,[hwndTip], HWND_TOPMOST,0,0,0,0,\
- SWP_NOMOVE+SWP_NOSIZE+SWP_NOACTIVATE
- @@:
- ; Субклассировать окно для вывода подсказки
- invoke SetWindowLong,[hwnd],GWL_WNDPROC,ToolTipProc
- mov edi,[lpData]
- stosd
- mov eax,[lpText]
- stosd
- ; Сохранить в пользовательских данных окна указатель на массив
- invoke SetWindowLong,[hwnd],GWL_USERDATA,[lpData]
- popa
- ret
- ttclass db 'tooltips_class32',0
- szNull db ' ',0
- endp
Обычная подсказка появляется при наведении курсора на элемент и остается видимой заданное время. Но гораздо лучше выглядит подсказка, которая перемещается за курсором. Субклассированная процедура обработки окна как раз отслеживает перемещение курсора мыши и перемещает окно подсказки следом за ним. Адрес текста подсказки и адрес предыдущего обработчика берется из пользовательских данных окна.
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------------------
- ; Субклассированная процедура обработки всплывающей подсказки
- ;----------------------------------------------------------------------
- proc ToolTipProc hwnddlg:DWORD,msg:DWORD,wparam:DWORD,lparam:DWORD
- cmp [msg],WM_MOUSELEAVE
- je .wmleave
- cmp [msg],WM_MOUSEMOVE
- je .wmmouse
- ; Получить адрес предыдущего обработчика
- invoke GetWindowLong,[hwnddlg],GWL_USERDATA
- mov eax,[eax]
- ; Передать управление предыдущему обработчику
- invoke CallWindowProc,eax,[hwnddlg],[msg],[wparam],[lparam]
- ret
- .wmleave:
- ; Скрыть подсказку
- invoke SendMessage,[hwndTip], TTM_TRACKACTIVATE, FALSE, ttip
- mov [TTipFlag],FALSE
- jmp .finish
- .wmmouse:
- ; Подсказка уже показывается?
- cmp [TTipFlag],TRUE
- je @f
- ; Установить перехватчик мыши на событие
- mov [tme.cbSize],sizeof.TRACKMOUSEEVENT
- mov eax,[hwnddlg]
- mov [tme.hwndTrack],eax
- mov [tme.dwFlags],TME_LEAVE
- invoke TrackMouseEvent,tme
- ; Получить адрес строки подсказки
- invoke GetWindowLong,[hwnddlg],GWL_USERDATA
- mov eax,[eax+4]
- ; Установить новый текст подсказки
- mov [ttip.lpszText],eax
- ; Обновить подсказку
- invoke SendMessage,[hwndTip], TTM_SETTOOLINFO, TRUE, ttip
- ; Включить отображение подсказки
- invoke SendMessage,[hwndTip], TTM_TRACKACTIVATE, TRUE, ttip
- ; Установить флаг, что подсказка уже отображается
- mov [TTipFlag],TRUE
- @@:
- ; Получить координаты мыши
- mov esi,[lparam] ; X
- and esi,0FFFFh
- mov edi,[lparam] ; Y
- shr edi,16
- ; Фактическое движение мыши было?
- cmp esi,[oldX]
- jne @f
- cmp edi,[oldY]
- je .finish
- @@:
- ; Сохранить новые координаты
- mov [pt.x],esi
- mov [oldX],esi
- mov [pt.y],edi
- mov [oldY],edi
- ; Вычислить абсолютные координаты на экране
- invoke ClientToScreen,[hwnddlg],pt
- mov ebx,[pt.y]
- ; Получить высоту окна подсказки
- invoke GetWindowRect,[hwndTip],trect
- mov eax,[trect.top]
- sub eax,[trect.bottom]
- add ebx,eax
- shl ebx,16
- add ebx,[pt.x]
- add ebx,5
- ; Установить подсказку на новые координаты
- invoke SendMessage,[hwndTip], TTM_TRACKPOSITION, 0, ebx
- .finish:
- xor eax,eax
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ...
- ; Получить хэндл элемента окна
- invoke GetDlgItem,[hwnddlg],ID_EDIT1
- stdcall AddTooltip,eax,szTip1,tt_data+(0*8)
- ; Получить хэндл элемента окна
- invoke GetDlgItem,[hwnddlg],ID_EDIT2
- stdcall AddTooltip,eax,szTip2,tt_data+(1*8)
- ; Получить хэндл элемента окна
- invoke GetDlgItem,[hwnddlg],IDCANCEL
- stdcall AddTooltip,eax,szTip3,tt_data+(2*8)
- ...
Просмотров: 5676 | Комментариев: 10
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Марат
(23.06.2012 в 01:45):
Нашел пример Iczelion'а(tooltips/тоже на FASM'e) у него работает...
Марат
(21.06.2012 в 14:50):
У меня не работает :(
Дмитрий
(22.01.2012 в 11:15):
У меня работает в Windows 7 x86
disciple27
(05.10.2011 в 20:18):
Тоже не работало, я полдня сидел думал... система - WinXP чистая.
Потом краем глаза увидел что олька проецирует comctl32.dll на процесс и... хорошо что стенка рядом была :)))))))))
Потом краем глаза увидел что олька проецирует comctl32.dll на процесс и... хорошо что стенка рядом была :)))))))))
ManHunter
(21.09.2009 в 23:39):
Странно, у меня и без него прекрасно работает. На всякий случай добавил в статью и исходники.
Zummenix
(21.09.2009 в 21:41):
Проблема решилась добавлением флага TTF_SUBCLASS к флагу TTF_IDISHWND :)
Zummenix
(21.09.2009 в 20:46):
Нет, система чиста.
Пример из уроков изцелона работает нормально.
Скорей всего у меня проблемы где-то
Пример из уроков изцелона работает нормально.
Скорей всего у меня проблемы где-то
ManHunter
(20.09.2009 в 23:16):
Все прекрасно работает на WinXP и Win7 x64/x86. Может у тебя по винде какой-то твикер прошелся, типа "не показывать всплывающих советов"?
Zummenix
(20.09.2009 в 19:49):
У всех работает tooltip в примере?
У меня не работает не только этот, но и свой, который я копаю уже второй день :)
У меня не работает не только этот, но и свой, который я копаю уже второй день :)
Добавить комментарий
Заполните форму для добавления комментария
Хм, у меня не работало в XP (в Win 8.1 x64 работает) - решилось добавлением манифеста и вызова InitCommonControls (тоже одна из особенностей XP - в той же Win 8.1 InitCommonControls не всегда нужна, тогда как в XP всегда (например, какой-нибудь простой лоадер, выводящий MessageBox, если файл не найден - в XP, если лоадер с манифестом, но без InitCommonControls, то сообщение не будет выведено)).
p.s. Проверил в XP после того, как добавил подсказки в один из патчей - в патче всё прекрасно работало на XP, т. к. манифест и InitCommonControls в нём уже были.
Спасибо ещё за одну полезную вещицу для gui. Патчи и т. п. пишу в юникоде, иногда бывают трудности с переносом ansi-функций, но тут ничего и править не понадобилось - единственное, пришлось добавить TTM_SETTOOLINFO equ TTM_SETTOOLINFOW и всё. :-)