Руководство программиста в Photon (953920), страница 61
Текст из файла (страница 61)
event –>flags |= Ph_EVENT_DIRECT;
Вот пример:
static void send_key( long key ) {
struct {
PhEvent_t event;
PhRect_t rect;
PhKeyEvent_t pevent;
} new_event;
PhEvent_t event;
PhKeyEvent_t key_event;
PhRect_t rect;
rect.ul.x = rect.ul.y = 0;
rect.lr.x = rect.lr.y = 0;
memset( &event, 0, sizeof(event));
memset( &key_event, 0, sizeof(key_event) );
event.type = Ph_EV_KEY;
event.emitter.rid = Ph_DEV_RID;
event.num_rects = 1;
event.data_len = sizeof(key_event);
event.input_group = 1;
key_event.key_cap = key;
key_event.key_sym = key;
if ( isascii( key ) && isupper( key ) ) {
key_event.key_mods = Pk_KM_Shift;
}
/* Генерирование нажатия клавиши */
key_event.key_flags = Pk_KF_Sym_Valid | Pk_KF_Cap_Valid | Pk_KF_Key_Down;
PhEmit( &event, &rect, &key_event );
/* Генерирование отпускания клавиши */
key_event.key_flags &= ~(Pk_KF_Key_Down | Pk_KF_Sym_Valid) ;
PhEmit( &event ,&rect, &key_event );
return;
}
Координаты события
Когда генерируется событие, координаты его прямоугольника устанавливаются относительно начала координат генерирующего региона. Но когда событие принимается, его координаты становятся относительными к началу координат региона-накопителя.
Менеджер Photon'а обеспечивает это путём соответствующего пересчёта координат. Член translation в структуре PhEvent_t задаёт пересчёт между началами координат генерирующего региона и региона-накопителя.
Обработчики события – необработанные и отфильтрованные ответные реакции
Виджетный класс PtWidget предоставляет для обработки событий такие ответные реакции:
Pt_CB_FILTER | Вызывается перед тем, как событие обработано виджетом. Это позволит Вам осуществить на основании события действие до того, как виджет обнаружит событие. Это также даст Вам хорошую возможность принять решение о том, надо ли событие проигнорировать, снять или позволить виджету его обработать. |
Pt_CB_RAW | Эти ответные реакции вызываются после того, как виджет обработал событие, даже если методы виджетного класса это событие поглощают. |
Эти ответные реакции вызываются каждый раз, когда принимается событие Photon'а, совпадающее с маской событий (предоставленной приложением). Поскольку все классы виджетов библиотеки виджетов Photon'а являются потомками класса PtWidget, эти ответные реакции могут использоваться любым виджетом библиотеки виджетов Photon'а.
Когда Вы прикрепляете к виджету необработанную или отфильтрованную ответную реакцию, библиотека виджета создаёт, если это необходимо, регион, который будет отлавливать для виджета заданные события. Это увеличивает количество регионов, которыми должен управлять менеджер Photon'а, и как результат, может приводить к понижению производительности.
Из этих соображений используйте обработчики событий только тогда, когда Вам надо делать нечто, что не может быть выполнено с помощью стандартных ответных реакций виджета. Если Вы всё-таки используете обработчики событий, рассмотрите возможность использования их только в оконных виджетах, которые уже имеют регионы.
Каждый раз, когда поступает событие Photon'а, оно спускается по иерархии семейства виджета до тех пор, пока виджет его не поглотит. (Когда виджет обработал событие и исключил взаимодействие другого виджета с этим событием, говорят, что первый виджет поглотил событие).
В основном ответные реакции Pt_CB_FILTER вызываются при проходе вниз по иерархии, а ответные реакции Pt_CB_RAW – при проходе вверх. Каждый виджет обрабатывает событие подобным образом:
-
Ответные реакции Pt_CB_FILTER виджета вызываются, если тип события совпадает с маской ответной реакции. Код возврата ответной реакции указывает, что произошло с событием:
Pt_CONSUME | Событие поглощено, без обработки методами класса виджета. |
Pt_PROCESS | Методам класса виджета было позволено обработать событие. |
Pt_IGNORE | Событие проигнорировало виджет и всех его потокомков, как будто их и не существовало. |
-
Если виджет чувствителен к событию и разрешена ответная связь Pt_CB_FILTER, метод виджетного класса обрабатывает событие. Метод класса может поглотить событие.
-
Если виджет поглотил событие, вызываются ответные реакции Pt_CB_RAW – если тип события совпадает с маской ответной реакции. Необработанные ответные реакции родителей виджета не вызываются.
-
Если виджет не поглотил событие, событие проходит на потомков виджета, если таковые имеются.
-
Если никакой виджет не поглотил событие, оно проходит обратно по иерархии семейства, и вызывается каждая ответная реакция Pt_CB_RAW виджета (если тип события совпадает с маской ответной реакции). Значение, возвращаемое ответной реакцией Pt_CB_RAW виджета, указывает, что произошло с событием:
Pt_CONSUME | Событие поглотилось, и при прохождении наверх к родителю виджета никакие другие необработанные ответные реакции не вызывались |
Pt_CONTINUE | Событие прошло наверх к родителю виджета |
Если виджет отключён (напр., в его флагах Pt_ARG_FLAGS выставлен флаг Pt_BLOCKED), необработанные и отфильтрованные ответные реакции не вызываются. Вместо них вызываются (если имеются) ответные реакции Pt_CB_BLOCKED виджета.
Давайте рассмотрим простенькое семейство виджетов, чтобы посмотреть, как это всё работает. Допустим, Вы имеете окно, содержащее панель, которая содержит кнопку. Вот что обычно происходит, когда Вы щёлкаете по кнопке:
-
Вызываются ответные реакции Pt_CB_FILTER окна, но событие не поглощается. Методы класса виджета тоже не поглощают событие.
-
Событие проходит к панели. Ни её ответные реакции Pt_CB_FILTER, ни её методы класса не поглощают событие.
-
Событие проходит к кнопке. Её ответные реакции Pt_CB_FILTER не поглощают событие, но это делают методы её класса; вызывается соответствующая ответная реакция (напр., Pt_CB_ACTIVATE).
-
Для события вызывается ответная реакция Pt_CB_RAW кнопки.
-
Ответные реакции Pt_CB_RAW панели и окна не вызываются, поскольку кнопка поглотила событие.
Если ответная реакция Pt_CB_FILTER панели указывает проигнорировать событие:
-
Окно обрабатывает событие, как и раньше
-
Ответная реакция Pt_CB_FILTER панели указывает проигнорировать событие, так что панель и все её потомки пропускаются.
-
Больше виджетов в семье нет, так что вызывается ответная реакция Pt_CB_RAW окна.
Более подробно о добавлении обработчиков событий смотри в:
-
разделе "Обработчики событий – необработанные и отфильтованные ответные реакции" в главе "Редактирование ресурсов и ответных реакций в PhAB";
-
разделе "Обработчики событий" главы "Уравление виджетами в программном коде приложения".
Накопление событий
Большинство приложений набирают события через вызов функции PtMainLoop(). Эта подпрограмма обрабатывает события Photon'а и поддерживает рабочие процедуры и обработку ввода. Если в Вашем приложении виджеты не используются, Вы можете накапливать события:
-
асинхронно, вызывая функцию PhEventRead(). Перед тем, как в первый раз вызвать PhEventRead(), Вы должны вызвать функцию PhEventArm().
-
синхронно, вызывая функциюPhEventNext(). Вы можете проверять наличие событий без блокировки, вызывая PhEventPeek().
Однако написание Вашей собственной функции главной петли не является тривиальной задачей; проще создать отдельный виджет (такой как PtRegion или PtWindow) и затем использовать PtMainLoop().
Функция PhGetRects() получает набор прямоугольников, и функция PhGetData() – порцию данных для события.
Регион может накапливать данное событие, только если часть региона пересекает событие, и регион чувствителен к этому типу события.
Сжатие событий
Менеджер Photon'а сжимает события перетаскивания, границ и мыши. То есть, если висит событие этого типа, когда поступает другое событие, новое событие будет подсоединено к необработанным событиям. В результате приложение видит для этих событий только самые последние значения и уберегается от накопления слишком большого количества ненужных событий.
Перетаскивание
Если Вам необходимо зафиксировать координаты мыши, например, для перетаскивания графических объектов в Вашем приложении, Вам понадобится работать с событиями.
Если Вам надо перенести произвольные данные внутри Вашего приложения или между приложениями, почитайте главу "Тащи и бросай".
Имеется два типа перетаскивания:
| Пользователь при перетаскивании видит контур. Когда перетаскивание завершено, приложение переставляет виджет. |
| Приложение перемещает виджет как продвигающееся перетаскивание. |
Перетаскивание выполняется в два этапа:
-
Инициализация перетаскивания, обычно когда пользователь щёлкает мышью на чём-то.
-
Обработка событий перетаскивания (Ph_EV_DRAG).
Эти шаги обсуждаются в нижеследующих подразделах.
Инициализация перетаскивания
Где Вы инициализируете перетаскивание, зависит от того, как пользователь собирается перетаскивать виджеты. Например, если пользователь удерживает нажатой левую кнопку мыши на виджете, чтобы его перетащить, перетаскивание инициализируется в ответной реакции Arm (Pt_CB_ARM) или Outbound (Pt_CB_OUTBOUND) виджета. Убедитесь, что в ресурсе Pt_ARG_FLAGS виджета установлен флаг Pt_SELECTABLE.
Перетаскивание начинается с вызова функции PhInitDrag():
int PhInitDrag(PhRid_t rid,
unsigned flags,
PhRect_t *rect,
PhRect_t *boundary,
unsigned int input_group,
PhDim_t *min,
PhDim_t *max,
const PhDim_t *step,
const PhPoint_t *ptrpos,
const PhCursorDescription_t *cursor );
где используемые аргументы:
rid | Идентификатор региона, с которым связаны rect и boundary. Вы можете получить его, вызвав функцию PtWidgetRid(). |
flags | Указывает, будет ли использоваться контурное или непрозразрачное перетаскивание, и какой край (края) перетаскиваемого прямоугольника оставляют след, что описано ниже. |
rect | Структура RhRect_t (см. "Справочник библиотечных функций Photon'а"), которая определяет область перетаскивания. |
boundary | Прямоугольная область, ограничивающая перетаскивание . |
input_group | Получить это можно из события в параметре cbinfo ответной реакции, вызвав функцию PhInputGroup(). |
min, max | Указатели на структуры типа Ph_Dim_t (см. "Справочник библиотечных функций Photon'а"), которые определяют минимальный и максимальный размеры перетаскиваемого прямоугольника. |
step | Ступенчатость перетаскивания. |
ptrpos | Если не NULL, то это указатель на структуру PhPoint_t (см. "Справочник библиотечных функций Photon'а"), который определяет начальную позицию курсора для перетаскивания. Приложения должны брать его от события, которое вызывает решение на начало перетаскивания. Если курсов переместился из этой позиции за время, когда Ваш PhInitDrag() достигнул Photon'а, Ваше перетаскивание соответствующим образом обновляется. Другими словами, Photon делает поведение перетаскивания таким, что оно как бы началось оттуда, где Вы думали был курсор, а не оттуда, где он был в действительности несколькими мгновениями позже. |
cursor | Если не NULL, определяет, как курсор должен выглядеть при перетаскивании. |
Если во flags включён Ph_DRAG_TRACK, используется непрозрачное перетаскивание; если ph_DRAG_TRACK не включён – контурное. Следующие флаги указывают, какой край (края) перетаскиваемого прямоугольника оставляет след:
-
Ph_TRACK_LEFT – левый
-
Ph_TRACK_RIGHT – правый
-
Ph_TRACK_TOP – верхний
-
Ph_TRACK_BOTTOM – нижний
-
Ph_TRACK_DRAG – все вышеперечисленные
Контурное перетаскивание
Следующий пример демонстрирует ответную реакцию Arm (Pt_CB_ARM), которая инициализирует контурное перетаскивание:
/* Запуск перетаскивания виджета */
/* Стандартные хеадеры */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Инструментальные хеадеры */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>