HTML Window

Sciter allows to create so called HTML windows and dialogs – check view.window() and view.dialog() functions.
While creating such windows you supply their content as HTML – either URL of HTML document or literal HTML string.

Provided HTML and CSS defines not only content of the window but it also can define type of window chrome (a.k.a. window’s frame) to use. Examples of different HTML window types:

standard-window window-layered flat-window transparent-window

In order to support window chrome definitions, Sciter was extended by the following HTML attributes:

window-frame attribute

window-frame attribute on root <html> element accepts the following values:

  • “none” or attribute omitted as whole – standard window , standard OS desktop window frame (with caption and close/maximize) buttons.
  • “solid” – frameless window with non-transparent background, just a rectangular box. Window chrome needs to be defined in markup (see below).
  • “solid-with-shadow” same as above but with shadow if OS supports it.
  • “extended” – window that allows to replace custom elements in window’s caption bar.
  • “transparent” – layered window, it’s shape is determined by background image or border-radius. CSS shall define html { background-color:transparent } for it.

See samples in sdk/samples/dialogs+windows/ folder.

window-resizable attribute

window-resizable attribute on root <html> element will make the window resizeable (e.g. WS_THICKFRAME on Windows).

view.windowMinSize() and view.windowMaxSize() methods allow to establish limits on window size.

window-blurbehind attribute

This attribute allows to create windows with blur-behind effect (note left side-bar):

glassy

window-blurbehind attribute accepts the following values:

  • "auto" – default OS blur-behind effect;
  • "ultra-dark", "dark", "light" and "ultra-light" – different level of ambient filter, screenshot above was made with window-blurbehind="dark".

Note: in order to see content of desktop through the window surface html (root element) shall have background-color:transparent defined on it.

window-icon="...url..." attribute

This attribute allows to define window icon – image that OS will use in window decoration and when the window is collapsed to taskbar.

Window chrome elements:

Some elements on custom window frames may have role attribute defined with the following values:

  • role=window-close – close button
  • role=window-icon – window icon button
  • role=window-minimize – minimize button
  • role=window-maximize – maximize/restore button
  • role=window-caption – window caption element – user can drag the window by pressing mouse in it.
  • role=window-body or <body> – area (four bands) between <html> and that body element is used for window resizing.

Existence of such elements determines WS_NCHITTEST handling on Windows.

Windows and desktop in script

Here is the list of methods and properties that are available for script to define and manipulate the window:

  • view.windowState – property allows to request and change window state: hidden, shown, maximized, full screen, etc.
  • view.windowCaption – property, caption of the window.
  • view.windowAspectRatio – property, defines width/height ratio of the window.
  • view.windowTopmost – property, instructs the window to appear on top of z-order.
  • view.windowResizable – property, allows the window to be sized by the user.
  • view.windowMaximizable – property, defines if window chrome contains “maximize” button.
  • view.windowMinimizable – property, defines if window chrome contains “minimize” button.
  • view.windowBlurbehind – property, defines level of blur-behind effect.
  • view.windowMinSize and view.windowMaxSize – functions, define boundaries in which window can be resized by the user.
  • view.windowIcon – property, allows to change window icon in run-time. Including script generated images.
  • view.windowMove() – move and size the window programmatically.
  • view.close() – request the window to be closed.

Methods that allow to create windows and dialogs:

  • view.window(params) and View.window(params) – create new owned (dependent) window or independent window (when it is called as a method of the class).
  • view.dialog(params) – create and show modal dialog. The function will wait until user will not close the dialog.
  • view.msgbox(params) – show modal message box.

Desktop/monitors related functions:

  • View.screens – property, reports number of monitors;
  • view.screen – property, reports monitor this window is on at the moment;
  • view.screenBox() – function, allows to get location and dimension of each monitor, its symbolic name or make a screenshot of the screen.

Auxiliary dialogs:

  • view.selectFile(params) ;
  • view.selectFolder(params) ;
  • view.selectPrinter(params) .

Using native child windows in Sciter.

Sometimes there is a need of using child windows in Sciter.
For example you would like to use Scintilla editor that is implemented on Windows as a child window component.

You can do that but with the following limitations:

  1. Elements with child window attached cannot appear inside scrollable containers as windows are drawn on top of everything. Only popup element can appear on top of child windows.
  2. Such elements cannot be under CSS transformations. Windows does not support affine transformations on child windows.

For such components to be useful in Sciter we will need to solve three tasks:

  1. Let Sciter know that we want custom functionality on that particular DOM element.
  2. Create such a window and attach it to the DOM element.
  3. Provide some component specific methods for the script to manipulate the component by script code.

To accomplish all three tasks we will need to define our own native behavior.

class editor: public sciter::event_handler 
{
   // ...
public:
   virtual void attached  (HELEMENT he ) //...
   virtual void detached  (HELEMENT he ) //...
}

struct editor_factory: public behavior_factory {

  editor_factory(): behavior_factory("editor") {}

  // the only behavior_factory method:
  virtual event_handler* create(HELEMENT he) { return new editor(); }

};

// instantiating and attaching it to the global list of supported behaviors
editor_factory editor_factory_instance;

We also need to instruct Sciter on which element it shall create an instance of our class editor.

Usually that is done by declaring special element type in CSS:

editor  /* element with tag 'editor' */
{ 
   behavior: editor;  /* will have that behavior */
   display: block;    /* displayed as rectangular block */
   width: *;          /* its width and height will take */
   height: *;         /* all available space inside the container */   
}

Having the above declaration we can define the editor in our markup as:

<body>
  <editor/>
</body>

Creating window and attaching it to the DOM element

When the engine will parse <editor/> element in markup it will find style for it. That style has behavior property defined for the element
so the engine will send SC_ATTACH_BEHAVIOR notification and default SC_ATTACH_BEHAVIOR handler ( see: sciter-x-host-callback.h ) will
request chain of registered behavior factories to create instance of the event_handler for the element. As a result our editor_factory::create() method will be invoked
At the very end the engine will call our editor::attached() method to indicate that the element has the editor attached to it.

Therefore the best place to call ::CreateWindow() Windows function is the attached() method of our behavior.

class editor : 
  public sciter::event_handler
{
public:

  HWND     hwnd;
  HELEMENT self; // note: weak ref (not addrefed)

  editor(): event_handler()
            , hwnd(0)
            , self(0) 
  {}

  virtual void attached  (HELEMENT he ) 
  {
    self = he;
    dom::element el = he;
    hwnd = ::CreateWindow(
                  TEXT("Scintilla"),
                  TEXT(""),
                  WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN | WS_VISIBLE,
                  0, 0,
                  0, 0,
                  el.get_element_hwnd(true), // get element's host window
                  0,
                  ghInstance,
                  0);
    el.attach_hwnd(hwnd); // attach the window handler to the DOM element.
                          // after that the engine will manage window position and dimension
                          // by CSS rules 
  } 

  virtual void detached  (HELEMENT he ) 
  { 
    if(hwnd && ::IsWindow(hwnd))
      ::DestroyWindow(hwnd);
    hwnd = 0;
    self = 0;
    dom::element el = he;
    el.attach_hwnd(0);
    delete this;   // we delete the handler here as no one is using it anymore.  
  }
  ...
} 

The behavior will manage life cycle of our window: it will create the window when DOM will be created and destroy it when the element will be removed from the DOM.

Defining scripting methods

The last task left is to define methods so our script can do something meaningful with the window.

In order to do that we need to define handle_scripting_call() in our behavior but instead of doing it that literally we will use BEGIN_FUNCTION_MAP/END_FUNCTION_MAP that provide
handle_scripting_call implementation for us:

class editor : 
  public sciter::event_handler
{
public:

  HWND     hwnd;
  HELEMENT self; // note: weak ref (not addrefed)

  editor(): event_handler()
            , hwnd(0)
            , self(0) 
  {}

  virtual void attached  (HELEMENT he ) { ... } 
  virtual void detached  (HELEMENT he ) { ... }

// scripting methods bindings
  BEGIN_FUNCTION_MAP
    FUNCTION_0("getText",get_text) // getText()
    FUNCTION_1("setText",set_text) // setText(text:string) 
  END_FUNCTION_MAP

  sciter::value get_text() {
    std::wstring text;
    // GetWindowText(hwnd) & friends here
    return sciter::value(text);
  }  

  sciter::value set_text(const sciter::value& text_val) {
    std::wstring text = text_val.get(L"");
    // SetWindowText(hwnd) & friends here
    return sciter::value(); // returns undefined value, a.k.a. void
  }  
}

This way we have defined our behavior that exposes two methods that we can use in script:

<script type="text/tiscript">
function self.ready() {
  var elEditor = $(editor);
  elEditor.setText("Hello world!"); // calling our native method
}
</script>
<body>
  <editor/>
</body>

In reality you probably will have more native methods defined. Just don’t forget to add their bindings to the FUNCTION_MAP.