Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Горячие клавиши


Автор: Letchik 21.6.2010, 11:27
Даже не знаю с чего начать...  smile 
Ну для начала всем привет!
Перебрал кучу вариантов, как можно легче реализовать поддержку горячих клавиш. Все методы работают, но у каждого есть один такой минус, который портит ВСЁ! Вот основные:

1) Акселераторы.
Плюс: легко и удобно.
Минус: Откликается во всех окнах приложения. Можно конечно это блокировать, но это будет "через попу".

2)RegisterHoKey()
Плюс: кажется, что леко и удобно.
Минус: Тот же что и у первого метода. В добавок, если работаю в другой программе, (например в скайпе сообщение хочу отправить кнопочкой Enter), кнопочка не работает. 

3)Локальный хук для каждого диалогового окна программы.
Плюс: Почти полный контроль над вводом с клавиатуры.
Минус: Сложно, не удобно, есть мелкие глюки, нейтрализовать которые очень проблематично.

Вопрос: как правильно реализовать поддержку горячих клавиш. Надо учитывать то, то одна и таже клавиша может использоватья и в главном окне, и в дочерном (например Enter или Insert), только выполнять она будет разные операции. 

Помогите кто может! smile 

Автор: Dem_max 21.6.2010, 11:37
Цитата

Даже не знаю с чего начать... 

Нужно сначала определиться для чего тебе горячие клавиши и что они должны делать. 

Автор: Letchik 21.6.2010, 11:45
Горячие клавиши не мне, а пользователю  smile 

Приведу пример:

Клавиша Insert в главном окне должна открывать диалоговое окно добавления новой записи. В этом диалоговом окне есть два варианта дальнейших действий. Можно нажать ОК (или Enter). Тогда запись добавитья и окно закроется. Можно нажать "Next" (или Insert), тогда запись добавитья, но окно не закроется, а освободится для ввода следующей записи.

Автор: GremlinProg 21.6.2010, 11:53
а что мешает перед вызовом диалога освободить хоткей Insert, 
после чего, в самом диалоге повторно его использовать, уже для другого действия,
а по закрытии диалога - снова назначить хоткей insert для работы в главном окне

Автор: Dem_max 21.6.2010, 11:55
тогда все проще, отлавливай WM_KEYDOWN и определяй код нажатой клавиши, в зависимости от открытого окна делай различные действия на тот же Insert.

Автор: Earnest 21.6.2010, 12:10
Действительно, зачем тебе горячие клавиши. Смысл их как раз в полной прозрачности - где бы ни нажал, выполняется некоторое действие. А локальная обработка - через ловлю WM_KEYDOWN, в каждом окне по своему.

Автор: Letchik 21.6.2010, 12:27
Именно это я и хотел сделать изначально.
Однако Enter скорее всего будет нажат тогда, когда курсор будет в каком-то эдитбоксе. А в таком случае WM_KEYDOWN не посылается окну родителю.

Автор: Peter 21.6.2010, 13:45
Когда-то такую проблему для себя решал так: в главном цикле сообщений проверял
Код
        if(messages.message == WM_KEYDOWN && messages.wParam == 13)
           if(messages.hwnd == hEdtSrc || messages.hwnd == hEdtDest
              || (bInComboVol && !bOpenComboVol))
              PostMessage(hwnd, WM_COMMAND, ID_DIV, 0);
Здесь hEdtSrc и hEdtDest - два эдит-бокса, а hwnd - главное окно. Еще две переменные показывают, находится ли фокус в комбобоксе и раскрыт ли этот комбобокс (а то если он раскрыт, то на Enter он сам должен отреагировать). Таким образом, программа сделает, что нужно при нажатии Enter, даже если фокус в окошечках редактирования hEdtSrc и hEdtDest.

Автор: TGrey 21.6.2010, 15:38
Цитата

Именно это я и хотел сделать изначально.
Однако Enter скорее всего будет нажат тогда, когда курсор будет в каком-то эдитбоксе. А в таком случае WM_KEYDOWN не посылается окну родителю.

А если самому послать это сообщение окну родителю?

Автор: Letchik 21.6.2010, 16:14
Цитата

А если самому послать это сообщение окну родителю?


С удовольствием, только когда? 

Автор: TGrey 21.6.2010, 16:53
Когда? Когда оно приходит возможно? Возможно заменить оконную процедуру Едита, ну или чего там, в ней проверять, что приходит, заодно выполнять нужные действия и в любом случае вызывать оригинальную процедуру. Только наверно в таком случае чем больше элементов, и то каждый надо обрабатывать, тогда им всем ставить одну процедуру(свою), а потом вызывать ихние.

Или может хук всунуть в приложение, пусть ловит все сообщения, а там уже фильтровать, что куда. 
А прочитал, что хук не устраивает. Просто не вижу в чем его проблема? 1 хук, 1 фильтрующая функция, в которой проверять нужные сообщения, по сути забивать на эту функцию и выполнять нужные себе действия.

Автор: Letchik 21.6.2010, 22:16
Цитата

Возможно заменить оконную процедуру Едита,

Это называется сабклассингом (Subclassing). Это я тоже пробовал. Но никому не советую использовать его в данных целях. Слишком много обработчиков получается. Дело в том, что обработчик Едита не подходит любому другому типу элементу управления (и наоборот). Для каждого типа прийдётся создавать свой обработчик. И это, извините, тоже через попу. 
Я склоняюсь к локальному хуку. Это сложно и кажется что неудобно, но я получаю почти полный контроль над вводом.
GremlinProg посоветовал освобождать хоткей при открытии диалога. Так не пойдёт. Пользователь должен иметь возможность работать в главном окне несмотря на открытое диалоговое окно.

Что касается локального хука. Есть пара идей, которые я реализую в ближайшее время. Как реализую - поделюсь опытом.
По любому, каждое диалоговое окно будет иметь свой хук. Однако хук действует на все открытые окна приложения. Поэтому вначале обработчика мы будем проверять, активно ли нужное нам окно. Если активно - обрабатываем нажатие клавиши, если нет - то это нам не надо.

Автор: TGrey 21.6.2010, 23:52
А какая разница в оконных процедурах Едита или чего либо? Структура то одинаковая. Я же не говорю все сообщения отлавливать. К примеру нужны же только клавиши, то есть только 1 сообщение, и в нем же и вообще в любом случае вызывать стандартный. По типу:
Код

...
switch(msg)
{
case WM_CHAR:
      //actions
      return CallWindowProc(....)
}
      return CallWindowProc(....)


То есть выполнятся нужные вам действия, а потом все пойдет дальше, как ни в чем не бывало, конечно если там тоже, что-то будет на эту кнопку обрабатываться, то получится не красиво=)

Ну а тогда если Диалог не модальный, тогда да... Горяие клавиши как-то ни к селу ни к городу=) Тогда точно, что надо проверять на активность окна. Хотя это тоже не ахти=)

Автор: Earnest 22.6.2010, 07:05
Вообще-то можно вместо хука вставить в главный цикл сообщений функцию-фильтр, что-то типа TranslateAccelerators, только свою. Можно разработать структуру данных, которая будет связывать хотя бы таблицу акселераторов с окном и использовать TranslateAccelerators. Или просто для каждого окна вызывать свой вариант фильтра. В общем, масса вариантов. А хук - это микроскопом гвозди. Я бы сказала, что в своем процессе хук нужно применять тогда, когда нужно добавить какую-то совершенно не связанную с функциональностью окна обработку. Или когда нужно ловить что-то в других, не подчиненных окнах.
В данном же случае, хук тебе потребуется не один - обработка-то разная. А если сделать просто каркас на основе таблиц соответствий - то функция-фильтр в основном цикле справиться ничуть не хуже.

Автор: Letchik 22.6.2010, 21:04
Всё-таки я решил попробовать усовершенствовать мой локальный хук. Представьте, получилось. Ниодин метод реализации горячих клавиш не даёт такой гибкости как хук. 
Казалось бы, это сложно, неудобно (только с первого взгляда), но всё совсем наоборот. Надо лишь проверять какое окно активно в данный момент. Если активно, то соответственно ввод произвёден именно в это окно. 
Решилось пару проблем касающихся TАВ"а. Например едит бокс со стилем ES_MULTILINE (что-то в этом роде) не отвечает на нажатие Таба. Поэтому я написал собственную обработку этой клавиши (Shift + TAB, TAB). Работает прекрасно.
В топике была идея использовать сабклассинг. Напоминаю, для данной цели он не подходит. Представьте, в диалоговом окне есть контролы трёх разных типов. Это значит, что для каждого типа контрола нужен свой обработчик. Три (!) обработчика, когда можно обойтись только одним. Сабклассинг полезен тогда, когда этот контрол выполняет что-то конкретное, что-то такое, что другие контролы не должны выполнять. А локальный хук полезен когда все контролы (или их большинство) отвечают одним и тем-же действием на некоторое сообщение. 
Короче. Я выбираю Хук!!!  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)