Выделение пунктов меню при наведении указателя мыши

Тема в разделе "WASM.WIN32", создана пользователем UnNamed, 18 ноя 2004.

  1. UnNamed

    UnNamed New Member

    Публикаций:
    0
    Регистрация:
    4 мар 2004
    Сообщения:
    6
    Адрес:
    Russia
    Уважаемые дзенствующие господа, после долгих и, что самое печальное, бесплодных попыток понять механизм работы стандартного меню (конкретнее: снятие выделения с пункта меню при перемещении указателя мыши за его пределы), обращаюсь к Вам в надежде получить желаемую информацию.



    Под стандартным меню подразумевается меню, которое создается системой, если при вызове CreateWindowEx() параметр hMenu содержал правильный идентификатор ресурса. Такое меню не является дочерним окном, а отображается как часть неклиентской области, в отличие от ReBar, CoolBar и пр.



    Техника исследования была следующей:

    1. запускался SoftIce, в нем устанавливалась точка останова на функцию SetCapture (а также на всякий случай на TrackMouseEvent и DragDetect), т.к. использование этой функции логично для определения момента выхода указателя за пределы заданной области,

    2. запускался стандартный "Калькулятор" Windows (это приложение имеет стандартное меню),

    3. запускался Microsoft Spy++, в нем отслеживались ВСЕ сообщения процесса "Калькулятор",

    4. окно Spy++ располагалось таким образом, чтобы перекрывать часть пункта меню "Help", видимой оставалась только "Hel". Активным было окно Spy++,

    5. указатель мыши перемещался к границе окна Spy++, затем смещался на один пиксель влево и оказывался над пунктом меню "Help" "Калькулятор"а,

    6. процесс "Калькулятор"а получал сообщения WM_NCHITTEST, WM_SETCURSOR, WM_NCMOUSEMOVE, и пункт меню "Help" выделялся, при этом не срабатывала ни одна точка останова в SoftIce (т.е. функции из п.1 не вызывались),

    7. указатель мыши смещался на один пиксель вправо, тем самым, оказываясь над окном Spy++, и, покидая видимые пределы окна "Калькулятор"а,

    8. выделение пункта меню "Help" автоматически снималось, при этом процесс "Калькулятор"а не получал никаких сообщений.



    Вопрос: каким образом окно получает необходимое для снятия выделения уведомление о том, что указатель мыши покинул пределы меню.

    Т.к. процесс не получает системных сообщений Windows, логично предположить, что вызывается некая функция обратного вызова. В таком случае интересно, какая функция WinAPI используется для указания callback процедуры.



    Предположение 2: Неужели функция выделения пунктов стандартного меню реализована в системе как реакция на сообщения драйвера мыши о перемещениях указателя?



    Интерес к данному механизму вызван необходимостью реализовать "подсветку" различных областей (кнопок) нестандартного окна.
     
  2. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    Для начала можно попробовать обработать эти сообщения (то есть не пропустить их в DefWindowProc), и посмотреть, действительно ли в них дело.
     
  3. Turkish

    Turkish New Member

    Публикаций:
    0
    Регистрация:
    25 окт 2004
    Сообщения:
    80
    Адрес:
    Russia
    Помоему меню в Винде реализоваано какими-то хитрыми внутренними механизмами, как и перетаскивание окна.
     
  4. vinnie_pooh

    vinnie_pooh New Member

    Публикаций:
    0
    Регистрация:
    30 июн 2004
    Сообщения:
    98
    Судя по всему, механизм выделения реализован так, что если какое-то окно получило WM_NCHITTEST (WM_MOUSEMOVE?), то при условии, что было выделено меню другого окна, это выделение снимается (в данном случае когда сообщение получал Spy++, то выделение "забиралось" у калькулятора). А за выделение меню точно отвечает WM_NCHITTEST , т.к. если его обработать таким образом:
    Код (Text):
    1.     cmp message, WM_NCHITTEST
    2.     je  return_0
    то меню не выделяется (и окно ни закрыть, не передвинуть).
     
  5. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.348
    WM_MOUSEHOVER, WM_MOUSELEAVE
     
  6. Chib777

    Chib777 Александр

    Публикаций:
    0
    Регистрация:
    22 сен 2003
    Сообщения:
    82
    Адрес:
    Ukraine, Odessa
    UnNamed



    Не совсем понял вопрос, но может поможет.

    Вот пример, отличная штука, несмотря на простоту, обнаружил что автор огромную работу провёл. Это пример не стандартного меню Windows, а "собственного" меню построенного на базе CreateWindowEx.



    [​IMG] _141292152__xpmenu.zip
     
  7. Chib777

    Chib777 Александр

    Публикаций:
    0
    Регистрация:
    22 сен 2003
    Сообщения:
    82
    Адрес:
    Ukraine, Odessa
    UnNamed



    Если речь идёт о пунктах меню верхнего уровня, то там перемещение мыши отслеживаеться через WM_NCHITTEST. Потом отправляеться сообщение в WM_DRAWITEM.

    Если речь идёт о пунктах меню верхнего уровня, то реально я думаю отследить передвижения. У меня почти получилось.



    Вот кусок кода:



    .elseif uMsg == WM_NCHITTEST ; сформируем сообщение

    ; Win95/98/me and Win NT 4.0



    call IsShellType

    .if eax == WIN_98 || eax == WIN_ME || eax == WIN_95 || eax == WIN_95 || eax == WIN_NT4



    invoke CallWindowProc,OldWndProc,hWin,uMsg,wParam,lParam

    .if eax == HTMENU && fAccel



    LOWORD lParam

    mov ptMnuItem.x, eax

    HIWORD lParam

    mov ptMnuItem.y, eax



    mov hMainMenu, $invoke( GetMenu, hWin )

    invoke MenuItemFromPoint, hWin, hMainMenu, ptMnuItem.x, ptMnuItem.y

    mov nItemIndex, eax



    .if nItemIndex != -1



    .if nPrevItemIndex != eax

    ; PrintDec eax, "WM_NCHITTEST"

    invoke DrawMenuBarItem, hWin, hMainMenu, nPrevItemIndex, 0 ; Clear the old Item

    .endif



    invoke DrawMenuBarItem, hWin, hMainMenu, nItemIndex, ODS_HOTLIGHT

    m2m nPrevItemIndex, nItemIndex



    .else



    invoke DrawMenuBarItem, hWin, hMainMenu, nPrevItemIndex, 0

    m2m nPrevItemIndex, -1

    .endif



    mov eax, HTMENU

    ret



    .endif



    .if nPrevItemIndex != -1 && fAccel

    mov hMainMenu, $invoke( GetMenu, hWin )

    invoke DrawMenuBarItem, hWin, hMainMenu, nPrevItemIndex, 0

    mov nPrevItemIndex, -1

    mov eax, HTMENU

    ret

    .endif



    .endif



    Код функции DrawMenuBarItem, не привожу, ничего особенного нет. Формирует сообщение WM_DRAWITEM, с соответствующей структурой DRAWITEMSTRUCT. Я это кусок недоделал, потом вылезли глюки. Мне это нужно было, т.к. в Win_ME и ниже, для пунктов верхнего уровня при наведении, не отправляеться сообщение WM_DRAWITEM.



    В предыдущем примере есть проблемка, автор для пунктов меню верхнего уровня сделал окно. На мой взгляд этого не нужно делать, тогда не узанть когда мышь покидает окно переходя в клиентскую область главного окна. Такое меню даёт немерянно нестандартных возможностей. Я было начал делать подобное меню, но сейчас остановился, наверно не буду доделывать.
     
  8. UnNamed

    UnNamed New Member

    Публикаций:
    0
    Регистрация:
    4 мар 2004
    Сообщения:
    6
    Адрес:
    Russia
    Господа! Спасибо за внимание к моему скромному вопросу.

    Прошу обратить внимание на постановку вопроса -

    Вопрос: каким образом окно получает необходимое для снятия выделения уведомление о том, что указатель мыши покинул пределы меню.



    Как отрисовать выделение при наведении ясно как божий день, а вот каким образом получить сигнал о том, что выделение надо снять? В обработчике WM_NCHITTEST это делать некорректно, т.к. WM_NCHITTEST не посылается окну, если указатель мыши из позиции над меню, одномоментно перемещается в точку за пределами окна (владеющего меню).



    Надеюсь на "вникновение" вами в суть вопроса.
     
  9. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    UnNamed

    Например, можно в отдельном потоке следить за координатами курсора мышки.