Эта глава описывает основы работы с HTMLayout.
Внимание!
Поехали.
Использование HTMLayout в качестве интерфейсной библиотеки предполагает следующие шаги:
Дескриптором экземпляра HTMLayout является обычный дескриптор окна (HWND).
Чтобы создать экземпляр HTMLayout, можно либо создать новое окно, либо “прицепить” библиотеку к уже существующему.
Создаем окно: HWDN hHtmlayoutWnd = CreateWindow(HTMLayoutClassNameT(), ...); //загрузка интерфейса из файла HTMLayoutLoadFile(hHtmlayoutWnd, L"путь/к/файлу/в/кодировке/UTF-16"); //или загрузка интерфейса из строки HTMLayoutLoadHtml(hHtmlayoutWnd, html_string_in_utf8_encoding, length_of_string_in_bytes);
Как правило при использовании htmlayout c GUI библиотеками (frameworks) типа MFC или WxWidgets удобнее “подмешать” функциональность htmlayout к уже существующему классу окна. Для этого достаточно в оконную процедуру окна (WndProc) добавить обработчик событий HTMLayout - до любых своих обработчиков.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT lResult; BOOL bHandled; lResult = HTMLayoutProcND(hWnd,message,wParam,lParam, &bHandled); if(bHandled) //если флаг handled установлен в TRUE return lResult; //значит вся необходимая обработка уже выполнена //собственные обработчики switch (message) { //... case WM_CREATE: { //загрузка интерфейса из файла HTMLayoutLoadFile(hHtmlayoutWnd, L"путь/к/файлу/в/кодировке/UTF-16"); //или загрузка интерфейса из строки HTMLayoutLoadHtml(hHtmlayoutWnd, html_string_in_utf8_encoding, length_of_string_in_bytes); break; } //... } return 0; }
HTMLayoutProcND обработает все события отрисовки и низкоуровневого пользовательского ввода (события клавиатуры и мыши), и даст вам возможность обработать остальные.
Дескриптор HTMLayout в этом случае совпадает с дескриптором того окна, к которому он “прицеплен”.
Теперь у вас на экране должно быть окно, в которое загружен ваш интерфейс (если вы все сделали правильно и не забыли вызвать ShowWindow).
Для того, чтобы двигаться дальше, надо понимать, что собой представляет “объектная модель” этого интерфейса. С точки зрения HTMLayout, в нее загружен документ, представленный “деревом” элементов (элементы DOM, Document Object Model elements). Каждый элемент в дереве соответствует одному тегу в исходном html-документе (это правило выполняется не всегда, исключения рассматриваются в следующей главе). Чтобы изменить внешний вид вашего интерфейса, как правило, нужно менять текст или аттрибуты элементов DOM, или удалять из дерева одни элементы и вставлять другие. Назначение обработчиков событий тоже выполняется относительно этих элементов.
Ниже описаны некоторые основы, подробнее работа с элементами DOM рассматривается в следующей главе.
Для того, чтобы получить доступ к дереву элементов нашего интерфейса, мы можем запросить “корневой” элемент: Plain С:
HELEMENT hRoot = NULL; HTMLayoutGetRootElement(hWnd, &hRoot); HTMLayoutUseElement(hRoot); // работа с hRoot... HTMLayoutUnuseElement(hRoot);C++:
using namespace htmlayout; dom::element root = dom::element::root_element(hWnd); // работа с root...
Имея корневой элемент, мы можем запросить любой элемент дерева при помощи различных функций навигации и поиска, например:
//запросим один из непосредственных дочерних элементов HELEMENT hBody; //поскольку hRoot соответствует тегу <html>, //его второй дочерний элемент будет соответствовать тегу <body> HTMLayoutGetNthChild(hRoot, 1, &hBody); //найдем первый элемент div с аттрибутом class = sidebar HELEMENT hDiv; HTMLayoutSelectElements( hRoot, //откуда начинать искать "div.sidebar", //CSS-селектор того, что мы ищем &first_finder, //функция, в которую будут передаваться найденные элементы &hDiv //этот параметр будет передан в first_finder, //сюда функция запишет адрес найденного элемента );
вот так может выглядеть функция first_finder из примера выше:
BOOL CALLBACK HTMLayoutElementCallback( HELEMENT he, LPVOID toStore ) { *((HELEMENT*)toStore) = he; //запишем адрес найденного элемента return TRUE; //вернем TRUE как сигнал, что дальше перебирать не надо. }
На С++ вышеприведенный фрагмент может быть записан несколько короче
:
using namespace htmlayout; dom::element sidebar = root.find_first("div.sidebar");
Можно поменять текст или HTML элемента:
HTMLayoutSetElementInnerText(hDiv, (LPCBYTE)"Hello, World!"); HTMLayoutSetElementHtml(hDiv, (LPCBYTE)"Hello, <b>World</b>!", 20, SIH_REPLACE_CONTENT);
Можно запросить или поменять один из его аттрибутов:
LPWSTR attr_value; HTMLayoutGetAttributeByName(hDiv, "class", &attr_value); HTMLayoutSetAttributeByName(hDiv, "class", L"new-class");
Можно удалить элемент:ы
HTMLayoutDeleteElement(hDiv);
Или создать и вставить в него дочерние элементы:
HELEMENT hP; HTMLayoutCreateElement("p", L"This is new paragraph", &hP); HTMLayoutInsertElement(hP, hDiv, 0); //вставим новый абзац как первый элемент нашего div
//допустим, мы изменили содержимое элемента hDiv, когда все готово, вызовем: HTMLayoutUpdateElement(hDiv, TRUE);
Чтобы назначить элементу интерфейса обработчик событий, существуют два способа: непосредственное назначение обработчика и связывание элемента с behavior (поведением).
Подробно обработка событий описана в главе Обработка событий, ниже всего несколько примеров.
Допустим, у нас есть некий элемент hButton, для которого мы хотим обработать событие “mouse down”. Выглядеть это будет примерно так:
//код функции-обработчика события: BOOL CALLBACK MouseHandler(LPVOID tag, HELEMENT target, UINT event, LPVOID prms ) { switch( evtg ) { case HANDLE_MOUSE: { MOUSE_PARAMS *p = (MOUSE_PARAMS *)prms; //специфичные для мыши параметры switch(p->cmd) //вот это - конкретное событие { case MOUSE_DOWN: //делаем то, зачем сюда пришли - обрабатываем событие return TRUE; } } } return FALSE; } //привязка обработчика к элементу: HTMLayoutAttachEventHandlerEx( hButton, //к кому цепляем обработчик &MouseHandler, //адрес обработчика NULL, HANDLE_MOUSE //какого рода события хотим обрабатывать );
Behavior (“поведение”) можно рассматривать как набор связанных обработчиков различных событий, относящихся к поведению одного элемента интерфейса. Например, “поле ввода” с точки зрения HTMLayout представляется как элемент <input type=“text”>, которое обрабатывает события мыши (изменяется положение каретки), клавиатуры, получения или потери фокуса и т.п. Каждый behavior в HTMLayout имеет свое имя, и связывается с каким-нибудь элементом интерфейса декларацией в таблице стилей. Например:
p.editable{ behavior: edit; }Теперь у любого абзаца с аттрибутом class=editable установлено поведение “строка ввода” - их можно редактировать. В ядре HTMLayout есть несколько встроенных поведений (edit, select, number и т.п.), но вы можете легко определить свои собственные. Для этого нужно обработать “уведомление” (notification) HLN_ATTACH_BEHAVIOR - см. ниже.
HTMLayout предоставляет программисту богатые возможности для изменения (перехвата) различных аспектов своего поведения. Например, можно перехвататить загрузку различных ресурсов, на которые ссылается HTML-файл (через img src=… или link src=…); создание контролов (например, ActiveX); подключение поведений; начало и окончание загрузки различных данных… Для всего этого существуют уведомления (notifications).
Для того, чтобы получать уведомления от HTMLayout, нужно в начале работы установить свой обработчик уведомлений:
HTMLayoutSetCallback(hWnd, &HTMLayoutNotifyHandler,0);
Вот таким может быть код обработчика:
LRESULT CALLBACK HTMLayoutNotifyHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LPVOID vParam) { NMHDR* phdr = (NMHDR*)lParam; switch(phdr->code) { case HLN_CREATE_CONTROL: //обрабатываем создание нестандартного контрола case HLN_ATTACH_BEHAVIOR: //обрабатываем запрос на подключение поведения case HLN_LOAD_DATA: //обрабатываем запрос на загрузку каких-нибудь данных, //например, картинки - можем загрузить ее из ресурсов //или БД } return 0; }
Вывод - это место, где автор устал писать. Похоже, описание получилось довольно сумбурным и неполным. Я очень надеюсь, что по мере появления вопросов у читателей и свободного времени у автора оно будет улучшаться и пополняться. Не стесняйтесь писать htmr-tutorial (сабака-барабака) imho.com.ua