Blog. Just Blog

Подсказки (tooltips) в диалоговых окнах

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Еще один способ придать вашим программам профессиональный вид - использование всплывающих подсказок (tooltips), которые появляются при наведении курсора мыши на какой-нибудь элемент диалогового окна. Готовые примеры на FASM или слишком громоздкие, или недостаточно гибкие, поэтому пришлось придумывать что-то свое. Причем с самого начала поставил себе задачу сделать функции работы с подсказками как можно более универсальными, чтобы в дальнейшем их можно было легко использовать в других проектах.

В сегменте данных определим все необходимые константы, переменные и структуры. Большая часть этих данных в FASM по умолчанию отсутствует, так что пришлось штудировать MSDN и другие источники и описывать их самостоятельно.
  1. ; Сегмент данных
  2. section '.data' data readable writeable  
  3.  
  4. hwndTip         dd ?    ; Хэндл окна подсказки
  5. TTipFlag        dd ?    ; Флаг активности подсказки
  6.  
  7. ; Структура TRACKMOUSEEVENT не определена, сделаем это сами
  8. struct TRACKMOUSEEVENT
  9.   cbSize        dd ?
  10.   dwFlags       dd ?
  11.   hwndTrack     dd ?
  12.   dwHoverTime   dd ?
  13. ends
  14.  
  15. ; Определяем нужные структуры
  16. tme     TRACKMOUSEEVENT
  17. pt      POINT
  18. ttip    TOOLINFO
  19. trect   RECT
  20.  
  21. oldX    dd ?    ; Сохраненные координаты мыши
  22. oldY    dd ?
  23.  
  24. ; Определяем нужные константы
  25. TTM_TRACKACTIVATE  = WM_USER + 17
  26. TTM_TRACKPOSITION  = WM_USER + 18
  27. TTM_SETMAXTIPWIDTH = WM_USER + 24
  28.  
  29. ; Сообщения окну подсказки
  30. TTF_SUBCLASS       = 0x0010
  31. TTF_TRACK          = 0x0020
  32. TTF_ABSOLUTE       = 0x0080
  33. TTF_TRANSPARENT    = 0x0100
  34.  
  35. ; Сообщение обработчика мыши
  36. TME_LEAVE          = 0x00000002
  37.  
  38. ; Максимальная ширина всплывающей подсказки
  39. TOOLTIP_WIDTH      = 200
  40.  
  41. ; Предустановленное количество элементов в массиве подсказок
  42. TOOLTIPS_COUNT     = 5
  43.  
  44. ; Массив данных для всплывающих подсказок
  45. ; Формат массива:
  46. ;   1 DWORD - сохраненный адрес обработчика
  47. ;   2 DWORD - адрес подсказки
  48. tt_data rd  (TOOLTIPS_COUNT*2)
Для добавления подсказок к элементам диалогового окна я написал отдельную процедуру, использующую субклассирование. Она же является процедурой, создающей исходное окно подсказки, если оно до этого не было создано. Окну подсказки присваивается максимальная ширина, определяемая константой TOOLTIP_WIDTH, и для большей изящности добавляется эффект тени.

Параметры вызова: hwnd - хэндл окна, к которому добавляется подсказка, lpText - указатель на строку подсказки в формате ASCIIZ, lpData - указатель на два DWORD (элемент массива), в которых будет храниться адрес предыдущей процедуры обработчика окна и указатель на строку подсказки.
  1. ;----------------------------------------------------------------------
  2. ; Процедура добавления подсказки к окну
  3. ; Входные параметры:
  4. ;   hwnd   - хэндл окна, к которому добавляется подсказка
  5. ;   lpText - указатель на строку подсказки
  6. ;   lpData - указатель на данные (2 DWORD)
  7. ;----------------------------------------------------------------------
  8. proc AddTooltip hwnd:DWORD, lpText:DWORD, lpData:DWORD
  9.         pusha
  10.  
  11.         ; Оно подсказки уже инициализировано?
  12.         cmp     [hwndTip],0
  13.         jne     @f
  14.  
  15.         invoke  GetModuleHandle,0
  16.         ; Создать окно подсказки
  17.         invoke  CreateWindowEx, NULL, ttclass, NULL, TTS_ALWAYSTIP,\
  18.                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,\
  19.                 NULL, NULL, eax, NULL
  20.         ; Сохранить хэндл окна подсказки
  21.         mov     [hwndTip],eax
  22.  
  23.         ; Определить константу CS_DROPSHADOW
  24.         CS_DROPSHADOW = 00020000h
  25.         ; Получить текущее значение стиля окна
  26.         invoke  GetWindowLong,[hwndTip],GCL_STYLE
  27.         ; Добавить к нему атрибут тень
  28.         or      eax,CS_DROPSHADOW
  29.         ; Установить новый стиль окна
  30.         invoke  SetClassLong,[hwndTip],GCL_STYLE,eax
  31.  
  32.         ; Заполнение структуры для подсказки
  33.         mov     [ttip.cbSize],sizeof.TOOLINFO
  34.         mov     [ttip.hwnd],NULL
  35.         mov     [ttip.uFlags],TTF_IDISHWND+TTF_TRACK+TTF_ABSOLUTE+TTF_SUBCLASS
  36.         mov     [ttip.uId],NULL
  37.         mov     [ttip.lpszText],szNull
  38.  
  39.         ; Создать подсказку
  40.         invoke  SendMessage,[hwndTip],TTM_ADDTOOL,0,ttip
  41.         ; Установить максимальную ширину. При этом длинный текст будет
  42.         ; переноситься на следующую строку
  43.         invoke  SendMessage,[hwndTip],TTM_SETMAXTIPWIDTH,0,TOOLTIP_WIDTH
  44.         ; Установить подсказку поверх всех окон
  45.         invoke  SetWindowPos,[hwndTip], HWND_TOPMOST,0,0,0,0,\
  46.                 SWP_NOMOVE+SWP_NOSIZE+SWP_NOACTIVATE
  47. @@:
  48.         ; Субклассировать окно для вывода подсказки
  49.         invoke  SetWindowLong,[hwnd],GWL_WNDPROC,ToolTipProc
  50.         mov     edi,[lpData]
  51.         stosd
  52.         mov     eax,[lpText]
  53.         stosd
  54.         ; Сохранить в пользовательских данных окна указатель на массив
  55.         invoke  SetWindowLong,[hwnd],GWL_USERDATA,[lpData]
  56.  
  57.         popa
  58.         ret
  59.  
  60. ttclass db 'tooltips_class32',0
  61. szNull  db ' ',0
  62.  
  63. endp
Адрес элемента массива привязывается к самому окну, сохраняясь функцией SetWindowLong в пользовательские данные. Процедура универсальная и позволяет добавлять подсказки к любым окнам. При этом надо следить, чтобы пользовательские данные окна GWL_USERDATA после установки обработчика больше не изменялись. Если подсказка добавлятся к элементу типа STATIC, то в ресурсах он должен быть описан с флагом SS_NOTIFY.

Обычная подсказка появляется при наведении курсора на элемент и остается видимой заданное время. Но гораздо лучше выглядит подсказка, которая перемещается за курсором. Субклассированная процедура обработки окна как раз отслеживает перемещение курсора мыши и перемещает окно подсказки следом за ним. Адрес текста подсказки и адрес предыдущего обработчика берется из пользовательских данных окна.
  1. ;----------------------------------------------------------------------
  2. ; Субклассированная процедура обработки всплывающей подсказки
  3. ;----------------------------------------------------------------------
  4. proc ToolTipProc hwnddlg:DWORD,msg:DWORD,wparam:DWORD,lparam:DWORD
  5.         cmp     [msg],WM_MOUSELEAVE
  6.         je      .wmleave
  7.         cmp     [msg],WM_MOUSEMOVE
  8.         je      .wmmouse
  9.  
  10.         ; Получить адрес предыдущего обработчика
  11.         invoke  GetWindowLong,[hwnddlg],GWL_USERDATA
  12.         mov     eax,[eax]
  13.         ; Передать управление предыдущему обработчику
  14.         invoke  CallWindowProc,eax,[hwnddlg],[msg],[wparam],[lparam]
  15.         ret
  16.  
  17. .wmleave:
  18.         ; Скрыть подсказку
  19.         invoke  SendMessage,[hwndTip], TTM_TRACKACTIVATE, FALSE, ttip
  20.         mov     [TTipFlag],FALSE
  21.         jmp     .finish
  22.  
  23. .wmmouse:
  24.         ; Подсказка уже показывается?
  25.         cmp     [TTipFlag],TRUE
  26.         je      @f
  27.  
  28.         ; Установить перехватчик мыши на событие
  29.         mov     [tme.cbSize],sizeof.TRACKMOUSEEVENT
  30.         mov     eax,[hwnddlg]
  31.         mov     [tme.hwndTrack],eax
  32.         mov     [tme.dwFlags],TME_LEAVE
  33.         invoke  TrackMouseEvent,tme
  34.  
  35.         ; Получить адрес строки подсказки
  36.         invoke  GetWindowLong,[hwnddlg],GWL_USERDATA
  37.         mov     eax,[eax+4]
  38.  
  39.         ; Установить новый текст подсказки
  40.         mov     [ttip.lpszText],eax
  41.         ; Обновить подсказку
  42.         invoke  SendMessage,[hwndTip], TTM_SETTOOLINFO, TRUE, ttip
  43.         ; Включить отображение подсказки
  44.         invoke  SendMessage,[hwndTip], TTM_TRACKACTIVATE, TRUE, ttip
  45.         ; Установить флаг, что подсказка уже отображается
  46.         mov     [TTipFlag],TRUE
  47. @@:
  48.         ; Получить координаты мыши
  49.         mov     esi,[lparam]     ; X
  50.         and     esi,0FFFFh
  51.         mov     edi,[lparam]     ; Y
  52.         shr     edi,16
  53.  
  54.         ; Фактическое движение мыши было?
  55.         cmp     esi,[oldX]
  56.         jne     @f
  57.         cmp     edi,[oldY]
  58.         je      .finish
  59. @@:
  60.         ; Сохранить новые координаты
  61.         mov     [pt.x],esi
  62.         mov     [oldX],esi
  63.         mov     [pt.y],edi
  64.         mov     [oldY],edi
  65.  
  66.         ; Вычислить абсолютные координаты на экране
  67.         invoke  ClientToScreen,[hwnddlg],pt
  68.         mov     ebx,[pt.y]
  69.  
  70.         ; Получить высоту окна подсказки
  71.         invoke  GetWindowRect,[hwndTip],trect
  72.         mov     eax,[trect.top]
  73.         sub     eax,[trect.bottom]
  74.         add     ebx,eax
  75.  
  76.         shl     ebx,16
  77.         add     ebx,[pt.x]
  78.         add     ebx,5
  79.  
  80.         ; Установить подсказку на новые координаты
  81.         invoke  SendMessage,[hwndTip], TTM_TRACKPOSITION, 0, ebx
  82. .finish:
  83.         xor     eax,eax
  84.         ret
  85. endp
Подключение подсказки выполняется следующим образом. В примере подразумевается, что структура tt_data инициализирована, как показано в начале статьи.
  1.         ...
  2.         ; Получить хэндл элемента окна
  3.         invoke  GetDlgItem,[hwnddlg],ID_EDIT1
  4.         stdcall AddTooltip,eax,szTip1,tt_data+(0*8)
  5.  
  6.         ; Получить хэндл элемента окна
  7.         invoke  GetDlgItem,[hwnddlg],ID_EDIT2
  8.         stdcall AddTooltip,eax,szTip2,tt_data+(1*8)
  9.  
  10.         ; Получить хэндл элемента окна
  11.         invoke  GetDlgItem,[hwnddlg],IDCANCEL
  12.         stdcall AddTooltip,eax,szTip3,tt_data+(2*8)
  13.         ...
В приложении пример диалогового окна с подсказками, добавленными к полям EDIT, кнопке, элементу STATIC и главному окну.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Tooltips.Demo.zip (5,030 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 5676 | Комментариев: 10

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
addhaloka (31.05.2017 в 16:13):
ManHunter
ЦитатаВсе прекрасно работает на WinXP

Хм, у меня не работало в 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 и всё. :-)
Марат (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 на процесс и... хорошо что стенка рядом была :)))))))))
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 в примере?
У меня не работает не только этот, но и свой, который я копаю уже второй день :)

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0043 сек.) / Память: 4.5 Mb
Наверх