- PVSM.RU - https://www.pvsm.ru -
Попросили вот здесь [1] про Sciter слово замолвить… Собственно вот рассказываю.
Sciter есть встраиваемый HTML/CSS/scripting engine для создания UI десктопных и мобильных приложений, классических так и [occasionally-]connected.
В принципе поддерживаются разные парадигмы приложений ограниченные лишь фантазией разработчиков. Например одной фирмой была сделана телефонная система со smart desktop phones на которых работал Sciter-based client — фактически специализированный browser загружающий UI (HTML,CSS, scripts и images) с системного контроллера станции по специализированному протоколу.
Другой пример: фирма Symantec использует sciter как UI для их consumer продуктов — Norton Antivirus со товарищи [2] (since 2007).

На картинке: sciter.exe demo проект из SDK + открытое окно DOM inspector'а, живет в inspector32.dll (исходники в SDK). inspector.dll можно использовать в своем проекте для отладки UI. Естественно что inspector UI есть опять же HTML/CSS/script + толика native code.
Под встраиваемостью имеются ввиду следующие базовые принципы:
Собственно процедура встраивания тривиальна. Это либо вызов ::CreateWindow(SciterClassName(),...), либо mix-in sciter'а к существующему окну добавлением в функцию окна (WinProc) такого вот кода:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//SCITER integration starts
BOOL r, handled = FALSE;
// delegating message processing to SciterProcND function:
LRESULT lr = SciterProcND(hWnd,message,wParam,lParam, &handled);
if( handled )
return lr;
//SCITER integration ends
switch (message)
{
case WM_CREATE:
//SCITER integration starts
{
// window was created, attaching callback function that will receive SCN_LOAD_DATA requests, etc.
SciterSetCallback(hWnd, BasicCallback, 0 /*cbParam is not ised in this sample*/ );
// loading default document:
LPCBYTE pb = 0; UINT cb = 0;
GetResource(L"default.html",pb,cb);
SciterLoadHtml(hWnd, pb,cb, NULL );
}
//SCITER integration ends
break;
.....
}
Когда функция SciterProcND получит WM_CREATE сообщение она создаст sciter instance для этого окна. После этого этот HWND можно использовать как Sciter engine handler для остальных sciter функций. SciterSetCallback(hwnd, callback), например, зарегистрирует callback функцию в которую будут приходить например все запросы на загрузку ресурсов HTML, CSS, scripts и images. Таким образом ваше приложение может как предоставить собственный загрузчик ресурсов так и пропускать запросы в Sciter и его встроенный http client.
В составе sciter SDK есть файл sciter-x-dom.h который содержит как plain API обявления функций доступа к DOM загруженного документа так и dom::element примитив для C++. Вот например как выглядит код читающий значение 'элемента ввода <input type=number id="bottles-of-beer">:
dom::element root = dom::element::root_element(hwnd);
dom::element numInput = root.find_first("input#bottles-of-beer");
json::value val = numInput.get_value(); // get numeric value
Sciter DOM API по своей функциональности повторяет jQuery, только исполнен «нативно».
Та же самая задача но в скрипте sciter'а ( используется tiscript [3] ):
var numInput = self.select("input#bottles-of-beer");
var val = numInput.value;
Приложение может описывать как свои собственные типы элементов и виджетов так и использовать набор готовых [4].
Native widget в коде приложения выглядит примерно так:
// sort of WinProc but for windowless DOM element:
class my_widget : public sciter::event_handler
{
// CTOR/DTOR called when this event_handler is attached/detached to/from DOM element
virtual void attached (HELEMENT /*he*/ ) { }
virtual void detached (HELEMENT /*he*/ ) { }
virtual bool handle_mouse (HELEMENT he, MOUSE_PARAMS& params ) { ... }
virtual bool handle_key (HELEMENT he, KEY_PARAMS& params ) { ... }
virtual bool handle_focus (HELEMENT he, FOCUS_PARAMS& params ) { ... }
virtual bool handle_timer (HELEMENT he,TIMER_PARAMS& params ) { ... }
virtual void handle_size (HELEMENT he ) { ... }
virtual bool handle_scroll (HELEMENT he, SCROLL_PARAMS& params ) { ... }
virtual bool handle_gesture (HELEMENT he, GESTURE_PARAMS& params ) { ... }
virtual bool handle_draw (HELEMENT he, DRAW_PARAMS& params ) { ... }
virtual bool handle_method_call (HELEMENT he, METHOD_PARAMS& params ) { ... }
virtual bool handle_event (HELEMENT he, BEHAVIOR_EVENT_PARAMS& params ) { ... }
// notification event: data requested by HTMLayoutRequestData just delivered
virtual bool handle_data_arrived (HELEMENT he, DATA_ARRIVED_PARAMS& params ) { ... }
virtual bool handle_scripting_call(HELEMENT he, SCRIPTING_METHOD_PARAMS& params ) { ... }
};
struct my_widget_factory: public sciter::behavior_factory
{
my_widget_factory(): behavior_factory("my-widget") {} // symbolic name for CSS
// create the instance of our widget:
virtual event_handler* create(HELEMENT he) { return new my_widget(); }
};
// in .cpp file:
my_widget_factory _my_widget_factory; // registering the factory in global list:
Названия методов говорят сами за себя поэтому коментировать их не буду. Прямая аналогия: event _handler это такой WinProc, но для windowless DOM элемента.
Подключение (binding) такого контроллера к DOM элементам выполняется декларативно с помощью CSS:
div.my-widget
{
border: 1px solid red;
behavior: my-widget; /* the name used in sciter::behavior_factory */
}
Т.е. как только в документе появится <div class="my-widget">...</div> ему назначится объявленный event handler и будет вызвана функция my_widget::attached(thatElement);.
Свои behaviors можно описывать также в скрипте. Там это еще проще:
class MyWidget : Behavior {
function attached() { this.state.visited = true; } // as an example
function detached() { this.state.visited = false; }
function onMouse(evt) {
switch(evt.type)
{
case Event.MOUSE_DOWN: ...
case Event.MOUSE_MOVE: ...
case Event.MOUSE_UP: ...
...
}
}
property value(v) {
get { return this.text; }
set { this.text = v; }
}
}
И в CSS:
div.my-widget
{
border: 1px solid red;
prototype: MyWidget; /* script class name */
}
После таких деклараций DOM элементу с таким behavior делается sub-classing, т.е. вот это работает:
var myWidget = self.select("div.my-widget");
myWidget instanceof MyWidget; // true, that DOM element is a MyWidget now.
var val = myWidget.value; // call of MyWidget.value/get.
В Sciter (h-smile core если быть точным) используется CSS level 2 плюс некоторые фичи из level 3. Также я добавил flow и flex-units [5] без которых использование CSS для именно desktop UI-строения занятие довольно проблематичное.
Исторически HTML и CSS используют т.н. endless tape модель — документ имеет ширину ограниченную шириной окна но высота документа не известна. Поэтому в CSS level 2 нет средств сказать «сделай высоту элемента равной высоте окна». Или, скажем, layout окна Outlook. В sciter такой layout описывается как:
<body>
<div id="mailboxes">...</div>
<div id="messages">...</div>
<div id="current-message">...</div>
</body>
и стиль
body {
flow: horizontal; /* content replaced horizontally */
width: *;
height: *; /* body spans whole window */
}
body > div {
height: *; /* all children of the body have the same height and take
whole height of the body */
}
Фактически flow описывает layout manager в терминах Java AWT [6]. В CSS level 3 появился Flexbox Module [7] который делает нечто аналогичное моему flow, но как-то коряво и не полно. Например Java::BorderLayout на нем похоже не сотворить.
В настоящее время есть две версии Sciter:
Обе версии используют совместимый API поэтому взаимозаменямы.
Основные отличия Sciter2:
<canvas> alike рисование (в bitmap buffer). Element.graphics() метод в Sciter1 сосздает такой буфер (для любого DOM элемента, а не только для ). В Sciter2 используется т.н. direct drawing. В скрипте описываются функции Element.onPaintContent(graphics), Element.onPaintBackground(graphics) которые вызываются в момент отрисовки. Фактически эквивалент WM_PAINT, WM_ERASEBACKGRND, etc. в Windows. Direct2D достаточно производителен чтобы это стало возможным..
Ну вот в двух словах про Sciter. Задавайте вопросы кому интересно.
Автор: csmile
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/html/17169
Ссылки в тексте:
[1] здесь: http://habrahabr.ru/post/153013/
[2] Norton Antivirus со товарищи: http://www.softpedia.com/progScreenshots/Norton-Internet-Security-Screenshot-8667.html
[3] tiscript: http://www.codeproject.com/Articles/33662/TIScript-language-a-gentle-extension-of-JavaScript
[4] набор готовых: http://www.terrainformatica.com/wiki/h-smile/built-in-behaviors/start
[5] flow и flex-units: http://www.terrainformatica.com/w3/flex-layout/flex-layout.htm
[6] layout manager в терминах Java AWT: http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
[7] Flexbox Module: http://www.w3.org/TR/css3-flexbox/
[8] transform: http://w3schools.com/cssref/css3_pr_transform.asp
Нажмите здесь для печати.