Embedding principles


Basics steps

6 steps you will need to do to integrate Sciter engine into your application:

  1. To create instance of Sciter engine,
  2. To load content in it (HTML with CSS and script references);
  3. To pass Windows messages to Sciter’s window procedure (optional, see below)
  4. To handle notifications (callbacks) from Sciter (optional).
  5. To change content of the window, call DOM methods, etc.
  6. You may want to implement native, your application specific, methods that are callable from script.

To create instance of Sciter you will need either to create new Sciter window or to attach (mix-in) Sciter engine to existing window.

Handle of the Sciter engine is defined as HWINDOW type which is:

  • HWND handle on Microsoft Windows.
  • NSView* – pointer to NSView instance that is a contentView of Sciter window on OS X.
  • GtkWidget* – pointer to GtkWidget instance that is a root widget of Sciter window on Linux/GTK.

Creation of Sciter instance and content loading

Method A. Creation of new window

// Creating the window using standard Windows way:
HWND hSciter = CreateWindow( SciterClassNameT(), ... );

// loading content from file or from URL:
SciterLoadFile(hSciter, L"file/path/or/URL/UTF-16");
// or loading HTML from memory buffer:
SciterLoadHtml(hSciter, html_buffer, length_of_buffer_in_bytes,L"base/url");

Method B. Adding Sciter functionality to existing window (mix-in)

When you use Sciter with existing frameworks like MFC, WTL, WxWidgets, WinForms it is more convenient to add Sciter functionality to existing system of window classes rather than to create full scale Window class for the Sciter from scratch. To do such a mix-in it is enough to modify window procedure (WndProc) of your class by adding call of SciterProcND() function before any other message handlers:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  LRESULT lResult;
  BOOL    bHandled;

  lResult = SciterProcND(hWnd,message,wParam,lParam, &bHandled);
  if(bHandled)      // if it was handled by the Sciter
    return lResult; // then no further processing is required.

  // your own message handlers
  switch (message)
  {
  //...
    case WM_CREATE:
    // case WM_INITDIALOG: - when Sciter is attached to some
    //                       dialog window (a.k.a. HTML dialog)
    {
      // SciterProcND has created instance of the engine for the HWND
      // while handling WM_CREATE thus we can load content:
      SciterLoadFile(hSciter, L"file/path/or/URL/UTF-16");
      // or from memory buffer:
      SciterLoadHtml(hHtmlayoutWnd, html_bytes, html_bytes_length,L"base/url");
      break;
    }
  //...
  }
  return 0;
}

This way SciterProcND() function will handle WM_PAINT and all low level input messages translating them into DOM events like MOUSE_DOWN, KEY_UP, etc.

Note: it is mandatory to pass WM_CREATE (or WM_INITDIALOG) and WM_DESTROY messages to the SciterProcND(). Instance of the engine is created when handling WM_CREATE and is destroyed on WM_DESTROY.

Resource handling and custom resource loader

HTML loaded into Sciter may contain external resources: CSS (Cascading Style Sheets), images, fonts, cursors and scripts. To get any of such resources Sciter will first send SC_LOAD_DATA notification to your application using callback function defined in code of your application. Your application can

  1. provide your own data for such resources (for example from resource section, DB or other storage of your choice)
  2. or delegate resource loading to built-in HTTP client and file loader
  3. or discard loading at all.

In order to receive SC_LOAD_DATA notification (and others like SC_CREATE_BEHAVIOR, SC_DATA_LOADED, etc.) you will need to define your own callback function and call SciterSetCallback(hwndSciter, yourCallback, ...); with it:

UINT CALLBACK HostCallback( LPSCITER_CALLBACK_NOTIFICATION pns,
                            LPVOID callbackParam )
  {
    callbackParam; // we are not using callbackParam in the sample,
                   // use it when you need this to be a method of some class
    switch(pns->code)
    {
      case SC_LOAD_DATA: return OnLoadData(LPSCN_LOAD_DATA(pns));
      case SC_DATA_LOADED: return OnDataLoaded(LPSCN_DATA_LOADED(pns));
      case SC_CALLBACK_HOST: return OnCallbackHost(LPSCN_CALLBACK_HOST(pns));
    }
    return 0;
  }
...
SciterSetCallback(hwnd, HostCallback, 0 /*cbParam is not ised in this sample*/ );

Note: SciterSetCallback() should be invoked before any SciterLoadHtml() call in order to send notifications while loading.

DOM access

Let’s assume you already integrated Sciter in your application and so you have Sciter window with loaded content (if you did this correct and didn’t forget to call  ShowWindow(hSciter) ).

From Sciter point of view loaded document is a tree of DOM elements ( elements of Document Object Model ). Sciter builds this tree while loading/parsing of input HTML. As a rule each tag in source HTML gets matching DOM element (there are exceptions, see below).

You can change text, attributes, state flags of DOM elements; add new or remove existing DOM elements. You also can attach your own DOM event handlers to DOM elements to receive events and notifications.

Therefore your UI in Sciter is a collection of uniform DOM elements that can be styled by CSS and manipulated by native or script code.

Basic operations

To access the DOM tree we need to get reference of its root element (root element is an element representing <html> tag in HTML source).

Getting DOM element reference

Here is how you will get its refence in plain C:

HELEMENT hRoot = NULL;
SciterGetRootElement(hWnd, &hRoot); // getting root reference per se
SciterUseElement(hRoot);
// do something with hRoot...
SciterUnuseElement(hRoot);

Note calls of SciterUse/UnuseElement functions in the code above. Sciter API functions return "weak" DOM handlers (HELEMENT) that are valid until you add/delete DOM elements. To preserve them from destroying (to make strong references) you need to call SciterUseElement when you need to store them and do SciterUnuseElement when references are not needed. This mechanism is very close to the pair of AddRef/Release used in COM.

File sciter-x-dom.h contains handy definition of HELEMENT "holder" object – class sciter::dom::element that de facto is a "smart pointer" – wrapper of  HELEMENT handle that does SciterUse/UnuseElement calls for you when it is needed.

And so in C++ the code above will look simply as:

using namespace sciter;
dom::element root = dom::element::root_element(hWnd);
// do something with the root...

By having root element reference we are able to access any other element in the tree using various access and search functions like  SciterGetNthChild, SciterSelectElements, … All of them are wrapped into methods of dom::element class in C++. Here is how you would get reference to first <div> element with class "sidebar" using CSS selectors:

using namespace sciter;
dom::element sidebar = root.find_first("div.sidebar");

The same in script:

var sidebar = self.select("div.sidebar"); // or
var sidebar = self.$(div.sidebar); // using stringizer select variant

DOM element operations

You can change text or HTML of DOM element:

dom::element el = ...;
el.set_text(L"Hello world"); // text
el.set_html(html_bytes, html_bytes_length); // inner html

The same but in script:

var el = ...;
el.text = "Hello world"; // text
el.html = "Hello <b>wrold</b>!"; // inner html

You can get or set DOM attributes of any DOM element:

string val = el.get_attribute("class");
el.set_attribute("class", L"new-class");

To remove existing DOM element (detach it from the DOM) you will do this:

el.detach();

and when code will live scope where the el variable is defined the DOM element will be destroyed.

Creation and population of DOM elements looks like this:

dom::element p = dom::element::create("p", L"Hello"); // create <p> element
el.append(p); // append it to existing element, or insert() ...

And in script:

var p = new Element("p", "Hello");
el.append(p);

To change runtime state flags of DOM element we do something like this:

el.set_state(STATE_VISITED);

And in script:

el.state.visited = true;

(after such call the element will match :visited  CSS selector)

Getting and setting values of DOM elements.

By default value of DOM element is its text but some DOM elements may have so called behaviors attached to them (see below). <input>’s elements for example are plain DOM elements but each input type has its own behavior assigned to the element. The behavior, among other things, is responsible for providing and setting value of the element. For example value of <input type=checkbox> is boolean – true or false, and value of <form> element is a collection (name/value map) of all inputs on the form.

In native code values are represented by sciter::value objects. sciter::value is a structure that can hold different types of values: numbers, strings, arrays, objects, etc.

Here is how to set numeric value of DOM element in native code:

dom::element num = root.find_first("input[type=number]");
num.set_value( sciter::value(12) );

In script the same will look like:

var num = self.select("input[type=number]");
num.value = 12;

And here is an example of setting values of multiple form fields in single call (in script):

var form = self.select("form.params");
var fields = { "first": 12,
               "second": new Date(2011,12,1) };
form.value = fields;

Behaviors and event handling.

Primary goal of User Interface (UI) as a subsystem is to present some information to the user and generate some events according to user’s actions. Your application handles UI events and acts accordingly executing its functions.

To be able to handle events in native code you will need to attach instance of sciter::event_handler class to existing DOM element or to the window itself. In the event_handler you will receive all events dispatched to the element and its children as before children (in SINKING phase) as after them (BUBBLING event phase), see Events Propagation . event_handler attached to the window will receive all DOM events no matter which element they are targeted to. Event.target field contains reference to target DOM element.

sciter::event_handler contains various methods – receivers of events of various types. You can override any of these methods in order to receive events you are interested in your implementation of sciter::event_handler class.

To attach native event handler to DOM element or to the window you can do one of these:

  1. "Manually", to Sciter window: sciter::attach_dom_event_handler( hwnd, your_event_handler_ptr )
  2. "Manually", to arbitrary DOM element: dom::element::attach_event_handler( your_event_handler_ptr )
  3. To group of DOM elements by declaration in CSS:
    selector { behavior:your-behavior-name }
    and defining in native code factory class that will create instances of your event handlers for such elements.
    See struct sciter::behavior_factory sources. The behavior_factory is designed to work as a default handler of SC_CREATE_BEHAVIOR notification. SC_CREATE_BEHAVIOR is invoked in all cases when the engine discovers that the element has declared behavior. Example sciter/sdk/api/behaviors/behavior_tabs.cpp demonstrates typical implementation of event_handler with behavior_factory.

You also can assign events handlers defined in script code:

  1. "Manually", individual events: if you have reference el of some element then to handle mouse events you can do this for example:
    el.onMouse = function(evt) { ... }
  2. "Manually", by assigning behavior class to the element:
    class MyEventsHandler: Behavior { ... }  // your behavior class
    el.prototype = MyEventsHandler; // "sub-class" the element.
  3. By declaration in CSS to all elements satisfying some CSS selector:
    selector { prototype: MyEventsHandler; }
    In this case MyEventsHandler class should be defined in one of script files loaded by your HTML.

Script and native code interaction

In Sciter you may want to define native functions that can be called by script. At the same time you may need to call script functions from native code. Sciter supports such interaction providing set of simple API functions:

Evaluating scripts and invoking script functions from native code

You can use one of these methods to call scripts from code of your application:

  • To evaluate arbitrary script in context of current document loaded into the window:
    json::value r = sciter::eval(hwnd,L"... script ...");
  • To call global function defined in script using its full name (may include name of namespaces where it resides):
    json::value r = sciter::call(hwnd,"namespace.name", p0, p1, ...);
    parameters – json::value’s
  • To call method (function) defined in script for particular DOM element:
    dom::element el = root.find_first(...);
    json::value r = el.call_method("method_name", p0, p1, ...);

Calling native code from script

If needed your application may expose some [native] functions to be called by script code. Usually this is made by implementing your own event_handler and overriding its on_script_call(helement, name, argc, argv, retval) method. If you will do this then you can invoke this callback from script as:

  • "global" native functions: var r = view.funcName( p0, p1, ... );  – calling  on_script_call of event_handler instance attached to the window.
  • As element’s methods: var r = el.funcName( p0, p1, ... ); – – calling  on_script_call of event_handler instance (native behavior) attached to the element.

This way you can establish interaction between scipt and native code inside your application.

In some cases you may also need to extend script itself by adding native classes and objects specific to your application. This is also possible by using primitives defined in tiscript.hpp file. Sciter SDK contains sources of tiscript-sqlite.dll – SQLite database engine wrapped as native and ready to use classes DB and Recordset.

All above is pretty much all that you need to know to start using Sciter in your applications. C/C++ header files in sciter/sdk/api/ folder contain documented specification of all API functions that you can use with you sciter instances.

On the right side-bar of this site you can find definitions of scripting classes and objects used in Sciter. SDK contains binary and sources of sciter.exe application – demo "player" with integrated help browser and DOM inspector. There are other integration samples in sciter/sdk/demos/ folder.