Sciter — встраиваемый HTML/CSS/scripting engine

    Попросили вот здесь про 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).

    image
    На картинке: sciter.exe demo проект из SDK + открытое окно DOM inspector'а, живет в inspector32.dll (исходники в SDK). inspector.dll можно использовать в своем проекте для отладки UI. Естественно что inspector UI есть опять же HTML/CSS/script + толика native code.


    Про встраиваемость



    Под встраиваемостью имеются ввиду следующие базовые принципы:
    1. Компактность, сейчас движок (sciter-x.dll) имеет размер 2 — 3mb
    2. Dependency free, sciter это одна DLL — sciter-x.dll. Не требует ничего сверх стандартной установки Windows.
    3. Универсальный и простой API. Использется т.н. plain Windows API. Ни COM ни .NET. Но Sciter можно использовать из например .NET или Delphi — любой среды понимающей plain API.
    4. Открытость и расширяемость основных механизмов. В коде приложения можно написать как свои собственные типы 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++. Вот например как выглядит код читающий значение 'элемента ввода
     :

    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 */
    }
    

    Т.е. как только в документе появится ... ему назначится объявленный 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:
    1. Sciter version 1 - GDI backend, все версии Windows включая Windows CE. Active maintenance mode.
    2. Sciter version 2 - Direct2D backend, Windows Vista и выше. Актуальная версия.

    Обе версии используют совместимый API поэтому взаимозаменямы.
    Основные отличия Sciter2:
    • Парсер и DOM HTML5 compatible.
    • Custom drawing механизм переработан. Sciter использует 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 живет здесь. Там же англоязычный форум про него.
      Русскоязычный форум про Sciter и HTMLayout живет на RSDN за что большое человеческое спасибо всей команде RSDN.
      Sciter2 SDK находится по этому адресу terrainformatica.com/sciter/sciter2-sdk.zip.

      Ну вот в двух словах про Sciter. Задавайте вопросы кому интересно.
    Поделиться публикацией

    Похожие публикации

    Комментарии 30

      0
      Пробовал, отличная штука, но очень бы хотелось туда обычный JS
        0
        Пишут, что их tscript это надмножество JS. Так что — какая разница?
          0
          Вот табличка сравнения: terrainformatica.com/sciter/js-dart-tis.htm
            0
            Да это не надмножество JS. Это пересекающееся множество. Т.е. JS библиотеки использовать скорее всего не выйдет.
        +1
        Не мешало бы ссылку на домашнюю страницу в статье.

        И «buy Sciter license and source code access» — вот это отталкивает.
          0
          И почему отталкивает? Ведь можно пользоваться бесплатно, если нужны сорцы и поддержка – плати. Помоему все честно.
            0
            Если так, то нормально. Только там нигде не написано, что можно использовать бесплатно.
              0
              Поправлю, спасибо.

              На пока:
              You may utilize Sciter Engine Software Product free of charge in any manner you see fit to build commercial or non-commercial applications and components.
            0
            «Не мешало бы ссылку на домашнюю страницу в статье».
            Хмм… точно помню писал ссылки. ок, еще раз тогда:

            Sciter home. Там же и англоязычный форум.
            Русскоязычный форум живет на RSDN. За что хлопцам оттуда большое человеческое спасибо.

            Ну и Sciter2 SDK.
            0
            Или я плохо читал, или оно не кроссплатформенно?
              +2
              На сайте написано, что только для Windows, Windows CE, Windows Mobile.
                0
                У меня т.н. customer driven разработка. Т.е. заказчики оплачивают. Пока никто не просил под другие платформы. Правда круг моих заказчиков несколько специфический: собственно все главные антивирусы (кроме KAV) и игровые фирмы.

                Кому нужен движок для специфической платформы — свистите, сделаем.
                  0
                  Sciter 3: Windows (XP и выше), Mac OS X (10.7 и выше), Linux (GTK 3 и выше)
                  0
                  Добровольно писать на HTML/JS вместо десктопных библиотек типа Qt?
                  Полгода использовал подобное, отладка — адище гребаное.
                    +1
                    Отладчиком HTML/JS пользоваться пробовали? Или alert-ами отлаживали? :-)
                    Впрочем, согласен, что использование HTML/JS для десктопного приложения должно быть оправдано какими то особыми целями. Иначе это странно. Как правило это возможность использовать часть функционала в мобильной или чисто Веб-версии приложения. И вот тут смысл в Sciter как то пропадает, потому что скриптовый язык не совместим с JS.
                      0
                      Ну тут смотря что, кто и как пишет.

                      Скажем вот люди пишут систему для прикроватных мониторов в больнице. Для них загружаемый и легко конфигурируемый UI — самое оно.

                      Или когда весь UI собирается из фрагментов писанных скажем в Норвегии, Индии и Японии, стиль разрабатывают в штатах, а потом на это дело садятся переводчики-локализаторы из Ирландии, то тут уж поневоле задумешься про то как это все делать малой кровью.

                      И про desktop приложения…
                      Если скажем пишешь что-то типа опросника для больницы или «морду лица» какой АСУ ТП то весь UI это набор правил типа «клик-здесь-там-раскрыть-по-дороге-подгузить-инфу-с-сервера». Собственно благородным програмированием такой UI automation назвать сложно.

                      $(button#show-details).onClick = function() {
                          $(div.slave-panel).state.expanded = true;
                          function dataReceived(data) {
                              this.value = data;
                          }
                          $(form#more-details).request("http://....",  dataReceived);
                      }
                      


                      и вообще, JS/jQuery как язык для UI automation это way to go. Можно и на C++ писать UI automation конечно но не сильно комфортно в определенных облястях.

                        0
                        Так JQuery в этом tscript живет или нет?
                          0
                          В чистом виде — нет, не портировал. Потребности не было.
                          JQuery селекторы имплементрованы нативно. А опять же за нативным методом Element.request() весь AJAX/JSON.

                          $( selector ) — возвращает первый элемент из документа удовлетвояющий селектору.
                          elem.$( selector ) — то же только из под-дерева elem.
                          $$( selector ) возвращает массив элементов удовлетвояющих селектору. Что-то сделать для всех таких элементов:
                          for( var elem in $$(ul#list > li) )
                             elem.state.visited = true;
                          

                          Как бы даже и по-гуманнее будет, нет?

                            0
                            Ничем не лучше:
                            $('ul#list > li').each(function(i,e){ 
                               e.state.visited = true;
                            });
                            

                            Смысл делать похожее API, а не совместимое? Это только путает.
                              0
                              Заметь, в моем варианте нет вызова функции котороя есть в принципе дорогое удовольствие да и syntax noise наличествует. А так если есть желание то можно и так написать:

                              $$(ul#list > li).each(function(i,e){ 
                                 e.state.visited = true;
                              });
                              

                              Но надо метод function Array.each(callback) {...} изобразить сначала.

                              Но как бы повторять jQuery задачи у меня не стояло изначально. Порт jQuery core в принципе пишется тривиально если есть такое желание. Моя нативная имплементация, кстати, наверное больше ближе к prototype.js чем к jQuery.
                                0
                                Добавил jquery micro port (в zepto спецификации)
                                Вот список методов www.terrainformatica.com/sciter/dom/q-doc.htm
                                См. {sciter-sdk}/samples/+query/
                        0
                        все хорошо но чем оно лучше Qt Webkit?
                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            Лучше/хуже вопрос относительный и зело субъективный.

                            Из объективных фактов:

                            1. Про размер уже сказали выше.
                            2. Desktop приложения используют другую security model чем browsers для которых safe browsing это главное. Соответсвенно ограничения в их движках. В качестве примера: в <richtext> редакторе (встроенный WYSIWYG HTML редактор) можно вставить картинку из clipboard. В том же webkit такой операции нет как класса.
                            3. sciter2 использует GPU для рисования, webkit рисует «руками».
                            4. webkit по рукам и ногам связан стандартами. Я же могу делать то что реально нужно в настоящий момент. В качестве примера фича по имени , print и print-preview в теле документа: image

                            0
                            Пользуемся Вашей предыдущей разработкой htmlayout, которая очень глючная и со множеством косяков. Надеюсь, этот продукт лучше.
                              0
                              Ну не без проблем конечно. Про глючная не слышал особо.
                              Вот например Avast! использует HTMLayout. Вполне так все работает у них. Наверное готовить HTMLayout умеют ;-)
                              0
                              Про чем оно лучше чем Qt, MFC и т.д.

                              Вот конкретный вопрос был про то как сделать <input type="hex-number"> — элемент ввода шестнадцатеричных чисел с кнопками "+" и "-".

                              В sciter/htmlayout такое делается как тривиальная сборка такого input из трех стандартных lego блоков (DOM элементов): с behavior:edit плюс две кноки . Вот решение полностью.

                              В Qt например это можно рещить написанием класса отнаследованным от QAbstractSpinBox со всеми вытекающими.
                              Проблема в том что еще нужно знать что есть такой вот QAbstractSpinBox, исследовать что он может и т.д.
                              В Sciter, т.е. HTML/CSS/script задача разбивается на две независимые группы: нарисовать стили, создать DOM струкуру из универсальных кубиков и связать события/состояния между ними.

                                0
                                Sciter3 работает на Windows (XP и выше), Mac OS X и Linux (GTK 3 и выше)
                                  0
                                  В свете всё большего использования ES6 табличка сравнения terrainformatica.com/sciter/js-dart-tis.htm видимо потеряла свою актуальность. Вы не планируете перенос плюшек ES6 в свой скрипт или переход на ES6 полностью?
                                    0
                                    Перенос плюшек планирую. Не всё, но вот скажем destructuring assignment:

                                    const jsonDate = "2015-01-01";
                                    var [year, month, day] = jsonDate.scanf("%d-%d-%d");
                                    


                                    сделаю ибо полезно. Как дополнение к существующему multi-return и multi-assignment:

                                    var x = 10, y = 20;
                                    (x,y) = (y,x); // swap values
                                    
                                    

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое