Попросили вот здесь про 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 со товарищи (since 2007).
На картинке: sciter.exe demo проект из SDK + открытое окно DOM inspector'а, живет в inspector32.dll (исходники в SDK). inspector.dll можно использовать в своем проекте для отладки UI. Естественно что inspector UI есть опять же HTML/CSS/script + толика native code.
Про встраиваемость
Под встраиваемостью имеются ввиду следующие базовые принципы:
- Компактность, сейчас движок (sciter-x.dll) имеет размер 2 — 3mb
- Dependency free, sciter это одна DLL — sciter-x.dll. Не требует ничего сверх стандартной учтановки Windows.
- Универсальный и простой API. Использется т.н. plain Windows API. Ни COM ни .NET. Но Sciter можно использовать из например .NET или Delphi — любой среды понимающей plain API.
- Открытость и расширяемость основных механизмов. В коде приложения можно написать как свои собственные типы DOM элементов и элементов ввода так и использовать свои собственные протоколы и механизмы загрузки ресурсов. К DOM tree можно обращаться как из скрипта так и из 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.
Манипулирование DOM
В составе 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 ):
var numInput = self.select("input#bottles-of-beer");
var val = numInput.value;
behaviors — расширения и компоненты
Приложение может описывать как свои собственные типы элементов и виджетов так и использовать набор готовых.
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.
CSS extensions
В Sciter (h-smile core если быть точным) используется CSS level 2 плюс некоторые фичи из level 3. Также я добавил flow и flex-units без которых использование 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. В CSS level 3 появился Flexbox Module который делает нечто аналогичное моему flow, но как-то коряво и не полно. Например Java::BorderLayout на нем похоже не сотворить.
Про версии Sciter.
В настоящее время есть две версии Sciter:
- Sciter version 1 — GDI backend, все версии Windows включая Windows CE. Active maintenance mode.
- Sciter version 2 — Direct2D backend, Windows Vista и выше. Актуальная версия.
Обе версии используют совместимый API поэтому взаимозаменямы.
Основные отличия Sciter2:
- Парсер и DOM HTML5 compatible.
- Custom drawing механизм переработан. Sciter использует
<canvas>
alike рисование (в bitmap buffer). Element.graphics() метод в Sciter1 сосздает такой буфер (для любого DOM элемента, а не только для ). В Sciter2 используется т.н. direct drawing. В скрипте описываются функцииElement.onPaintContent(graphics)
, Element.onPaintBackground(graphics) которые вызываются в момент отрисовки. Фактически эквивалент WM_PAINT, WM_ERASEBACKGRND, etc. в Windows. Direct2D достаточно производителен чтобы это стало возможным.
В CSS Sciter2 в полном объеме работает transform свойство. Direct2D вытягивает это дело.
.
Ну вот в двух словах про Sciter. Задавайте вопросы кому интересно.
Автор: csmile