Re: 2. Обработка событий: events and behaviors
От: Зверёк Харьковский  
Дата: 29.11.06 15:41
Оценка: 108 (7)
Эта глава посвящена обработке событий. "События" в терминах HTMLayout — это, в общем, именно то, что можно предположить логически: сообщения, посылаемые в результате каких-либо действий пользователя. HTMLayout посылает и "физические" события (кликнута мышь, нажата кнопка на клавиатуре), и "логические" (кликнута кнопка, изменился фокус). Все события разделены на несколько групп (мышь, клавиатура, фокус, рисование и т.п.), описанных в enum EVENT_GROUPS в файле htmlayout_behavior.h, подписаться можно на одну или несколько групп. Но, прежде чем мы поговорим о подписке на события и их обработке, вкратце разберемся, как и куда они посылаются.

Модель передачи событий в HTMLayout

HTMLayout реализует так называемую "event sinking/bubbling model" ("модель погружения/всплытия"). В ней событие проходит 2 раза: от всего окна к конкретному элементу, и от элемента к окну. Например, если есть вот такой html:
<html>
  <body>
    <p><button>click me, please!</button></p>
  </body>
</html>


...и пользователь кликает по кнопке "click me, please!", то событие MOUSE_DOWN | SINKING (это две константы, объединенные операцией "или") будет последовательно получено элементами DOM, соответствующими "html", "body", "p", "button", а затем, событие MOUSE_DOWN (уже без флага SINKING) будет последовательно получено элементами "button", "p", "body", "html".

В любом из этих элементов событие может быть обработано (если "повесить" соответствующий обработчик) и пропущено дальше (возвращением FALSE из обработчика) или остановлено (возвращением TRUE). Если событие "остановлено", оно считается обработанным и последующим элементам не передается. Причем, если остановить событие еще на стадии "погружения" (sinking), то и никакого "всплытия" не будет. Например, если повесить на элемент <html> обработчик клавиатурных событий и всегда возвращать из него TRUE, никакие другие элементы "видеть клавиатуру" не будут.

Такая модель позволяет очень гибко обрабатывать события, например, перехватить нажатие определенных клавиатурных сочетаний до того, как их получит какой-либо другой элемент; или наоборот, обработать только те нажатия, которые не были обработаны другими элементами.

Обработка событий

Для того, чтобы обработать событие, нужно "прицепить" к элементу DOM обработчик события Делается это функцией HTMLayoutAttachEventHandler или HTMLayoutAttachEventHandlerEx (первая подписывает на все события, последняя позволяет указать, на какие именно группы событий вы подписываетесь).

Внимание! Если подписаться на события смены фокуса (HANDLE_FOCUS), то элемент, к которому прицеплен обработчик, считается focusable (в него будет попадать фокус по Tab). Это не всегда то, что вы имели в виду. Поэтому функцией HTMLayoutAttachEventHandler (которая подписывает на все события) нужно пользоваться с осторожностью.

Обработчик события, который передается как аргумент в функцию HTMLayoutAttachEventHandler(Ex) — это C-функция со следующим прототипом:

BOOL CALLBACK ElementEventProc(LPVOID tag, HELEMENT he, UINT evtg, LPVOID prms);


Здесь he — хэндл того элемента DOM, к которому цеплялся обработчик, evtg — группа событий, prms — параметры события, для каждой группы событий они свои.

Замечание. Обратите внимание, что he — это элемент, к которому прицеплен обработчик, а не тот, с которым произошло событие. Для примера выше, если обработчик повешен на <html>, а кликнули по кнопке, то he будет равен хендлу <html>, а p->target в примере ниже — хэндлу <button>

Вот типичный "тупой" код обработчика всех событий (тупой — в смысле, в лоб):
BOOL CALLBACK naive_handler(LPVOID tag, HELEMENT he, UINT evtg, LPVOID prms )
{
  switch( evtg )
  {
    case HANDLE_MOUSE:
    {
      MOUSE_PARAMS *p = (MOUSE_PARAMS *)prms; //специфичные для мыши параметры

      HELEMENT target = p->target;//узнаем элемент, на который реально кликнули
      
      switch(p->cmd) //вот это - конкретное событие
      {
        case MOUSE_DOWN: ...
        case MOUSE_UP: ...
        case MOUSE_MOVE: ...
        ...
      }
    }
    case HANDLE_KEY:
    {
      //та же петрушка, в общем
      KEY_PARAMS *p = (KEY_PARAMS *)prms;
      HELEMENT target = p->target;
      switch(p->cmd){...}
    }
    ....
  }

  return FALSE;
}


Все структуры MOUSE_PARAMS, KEY_PARAMS и т.п.; константы, соответствующие конкретным событиям и прочая справочная информация доступна, опять же, в htmlayout_behavior.h

Замечание. Обратите внимание, что, если хотите поймать событие на стадии погружения, нужно проверять код события (p->cmd в коде выше) на наличие в нем флага SINKING (то есть на стадии погружения код события будет MOUSE_DOWN | SINKING).

Бережно относитесь к возвращаемому значению! Напоминаю, если вернуть TRUE, то событие дальше не пойдет.


Концепция behaviors (поведений)

Behavior (поведение) в модели HTMLayout — это именованный набор обработчиков событий, который можно "прицепить" к элементу DOM декларативно, написав в CSS:
behavior: name-of-my-behavior;


Встретив такую конструкцию в CSS, HTMLayout посылает notification HLN_ATTACH_BEHAVIOR, аргументом передавая "name-of-my-behavior", в обработчике которой можно сделать с соответствующим элементом что угодно (увы, глава по notifications мною еще не написана, она будет следующей).

Существуют несколько встроенных поведений, о которых знает HTMLayout, например, все контролы (поля ввода, чекбоксы, списки и т.п.) — это всего лишь поведения. То есть, если написать в CSS, к примеру:
td{
  behavior: edit;
}

...то получим таблицу с редактируемым текстом ячеек. Редактор в ячейках будет вести себя совершенно так же, как и <input type=text>. Самое смешное, что можно и наоборот:
input[type=text]{
  behavior: none;
}

...и поля ввода больше не будут таковыми

Замечание. На сайте HTMLayout есть такая вещь, как Master style sheet — таблица стилей, которые определяют внешний вид элементов по умолчанию. Там можно подсмотреть, какой набор стилей (в том числе behavior) определяет тот или иной элемент.

Интересная вещь, связанная с поведениями, определенными в CSS — можно легко изменить. Например, если определить в CSS стили таким образом:
td:current /*поведение edit только у ячейки в состоянии STATE_CURRENT*/
{
  behavior:edit;
}

...то меняя state (состояние) ячеек таблицы, мы меняем и их "редактируемость".

Напоследок остается сказать, что в HTMLayout SDK поставляются достаточно удобные C++-классы, облегчающие создание своих поведений (см. htmlayout_behavior.hpp).

В папке behaviors/ содержатся также несколько примеров поведений, созданных на основе этих классов (например, табы-закладки, разворачиваемые списки и прочие приятности).

Замечание. Поведения в папке behaviors/ не являются встроенными. То есть, чтобы использовать их в своей программе, придется слинковаться с соответствующими cpp-файлами и обработать HLN_ATTACH_BEHAVIOR.




Хватит покуда. Следующая глава — про notifications.
Оставляйте заявки, задавайте вопросы.
FAQ — це мiй ай-кью!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.