Sciter for AngularJS practitioners. Directives.

Directives in AngularJS

From AngularJS documentation :

At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS’s HTML compiler ($compile) to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children.

And here is a typical AngularJS directive implementation:

app.directive('myCustomer', function() {
  return {
    template: 'Name: {{name}} Address: {{address}}', // content
    scope: { name:"", address:"" },  // internal data model
    link: function(scope, elem, attrs) { // initialization 
      elem.bind('click', function() { ... });
      elem.bind('dblclick', function() { ... });
    }
  };
});

It says that each <my-customer> element will have

  • Name: ... Address: ... content;
  • click and dblclick event handlers.

In Sciter

Behaviors

Declarative code-to-element binding in Sciter is made by CSS. Sciter’s prototype property is used for that purpose:

my-customer {
  prototype: MyCustomer url(my-customer.tis);
  display: block;
  ...
}

In plain text: all <my-customer> elements are rendered as block element and will have class MyCustomer assigned to them.  The class will be loaded from my-customer.tis file (if it was not loaded before):

class MyCustomer : Element 
{
  function attached() // called when element gets this class - "link" in terms of Angular 
  {
     // content initialization: 
     this.$content(Name: <output.name/> Address: <output.address/>);
  }
  // event handlers:
  event click { ... /* 'this' here is the element*/ }
  event dblclick { ... }
}

As the code is assigned by CSS then you can use full power of CSS selectors to assign code classes to elements.

Aspects

Another option to bind code with elements is to use so called aspects. Aspect here is just a script function that gets executed when its selector matched the element first time:

[collapsible] { aspect: Collapsible url(my-aspects.tis); }
[collapsible]:collapsed > :last-child { display:none; } // last child is invisible when collapsed

And Collapsible here is a simple function that handles click event and triggers :collapsed state flag switch:

function Collapsible() { 
  this << event click {
     if( this.state.collapsed ) this.state.expanded = true;
     else this.state.collapsed = true;    
  }
}

Having such aspect defined we can define collapsible element:

<div collapsible>
  click here to see content
  <p>Content</p>
</div>

So if you have multiple collapsible sections in your design then just add “collapsible” attribute to them.

Sublime 3, Sciter syntaxes

Sciter HTML/CSS/script in Sublime3

Steps to add Sciter support to Sublime3

  1. In Sublime: menu Preferences » Browse Packages. It will open /Packages folder.
  2. Create folder named Sciter right in that folder.
  3. Download all *.tmLanguage files from github.com/svitalij/sciter.tm.syntax
  4. Copy those *.tmLanguage files into that folder.

Done. Now if you will open .tis files in Sublime3 you will see syntax highlighting.

To edit HTML and CSS files with Sciter specific extensions select

View » Syntax » Sciter » Sciter HTML

Gratitude to Vitalij Shitts the Great

HTML/CSS desktop UI solutions, distribution sizes

As was mentioned, I’ve added scapp.exe to the SDK distribution. The Scapp is a monolithic executable with Sciter engine inside.

The question: Having the scapp what if to create IDE using it? How that IDE will compare with other existing on the market?

To answer on this question I’ve created simple IDE sketch in Sciter (sdk/samples/ideas/ide/) :


ide-windows ide-osx
As you see it is pretty close to, let’s say, Brackets editor from Adobe: brackets-windows

My quick sketch implements just basic functionality: loading file, basic editing and syntax highlighting. All together HTML/CSS/scripts are about 40k (uncompressed). Full implementation will definitely have more of those. Let’s assume that we will be creative enough to produce 1mb of scripts, styles and HTML.

Together with the executable it will give us:

Binary (scapp.exe) HTML/CSS/scripts total
on disk 4.1mb 1mb 5.1mb
compressed
(distribution)
1.8mb 0.29mb 2.09mb

So the whole IDE could be of just 2 megabytes !

Let’s compare it with other behemoths then:

IDE/vendor distribution size installation size ratio to largest
(the less – the better)
Brackets / Adobe 48.1mb 114mb 51%/31%
Visual Studio Code / Microsoft 31.9mb 135mb 34%/37%
Atom / GitHub 93.6mb 366mb 100%/100%
Sciter / Terra Informatica Software 2.1mb 5.1mb 2%/1.4%

So for each of us, users, it takes 50 times more of traffic to download these things that it should be… Is it fair?

Components, React.js style…

legos

I am thinking about adding @component feature to Sciter a la React.js

Consider this construct:

@component Toggler {

  :root { flow: stack; }
  
  :root > option:nth-child(1) { ... }
  :root > option:nth-child(2) { ... }
  
  :root[on][off] :
  
    <option>{ attributes.on }</option> 
    <option>{ attributes.off }</option> 

  :root[on][off][mixed] :
  
    <option>{ attributes.on }</option> 
    <option>{ attributes.off }</option> 
    <option.mixed>{ attributes.mixed }</option> 

  // event handler 
  on :root click(evt) {
    ...
  }  
  // event handler on child element
  on option mousedown(evt) {
    ...
  }  

  // method  
  function foo() { ... }

  // property  
  property bar(v) { ... }

}

The @component is essentially a @set that contains not only style declarations but also [optional] markup of the component and code equivalent of

class Toggler:Behavior { 

   function attached() {
      this.$content(...); 
   }

   function foo() {  ... }
   property bar(v) { ... }
}

So if you have this markup:

<html> 
  <head>
    <link rel="components" href="components/">
  </head>
  <body>
    <toggler on="yey" off="ney">
  </body>
</html>

you will see that component instantiated and initialized in the DOM.

Question about script optimizations in Sciter

Code in question is this:

$(div#sidebarHeader).first.onClick = function()
{
   // Toggle collapse/expand icon
   $(div#sidebarHeader).first.attributes.toggleClass("icon-horizontal-collapse");
   $(div#sidebarHeader).first.attributes.toggleClass("icon-horizontal-expand");
}

Is it optimal? If not then how to make it better?

A bit about script implementation architecture in Sciter

Speed of parsing is as critical in Sciter cases as speed of execution. eval(source) function is a first class citizen as any other function. Sciter’s script implementation uses fast compiler producing bytecodes and VM executing those bytecodes. That’s pretty fast if taken as a whole. But bytecode optimization options are quite limited for the compiler as they take time of script loading.

Thus you should expect script statements to be executed literally.

So these two lines:

$(div#sidebarHeader).first.attributes.toggleClass("icon-horizontal-collapse");
$(div#sidebarHeader).first.attributes.toggleClass("icon-horizontal-expand");

will produce two pretty much identical bytecode sequences that will be executed one by one. Script is quite simple and will be executed fast enough though.

But common engineering sense is required, as usual .

The main problem in code above is not in the script itself but in three identical calls of $ function. Keep in mind that $(selector) execution is in general O(N) complex lookup. The engine may do some optimizations but in general element-by-selector lookup in that complex – to find particular element you may need to scan whole DOM tree to find first satisfying element.

These three calls can simply be replaced by just one:

$(div#sidebarHeader > :first-child ).onClick = function()
{
   // Toggle collapse/expand icon
   this.attributes.toggleClass("icon-horizontal-collapse");
   this.attributes.toggleClass("icon-horizontal-expand");
}

Yet $(div#sidebarHeader > :first-child ).onClick = ... assignment will be executed strictly once – at load time of the document.

And another common sense reasoning about DOM classes in this case…

We don’t need two independent classes here. Single “collapsed” class-flag will work just OK as element can be either expanded or collapsed but not in between of these two states. So our code will look as simple as this:

$(div#sidebarHeader > .icon-horizontal).onClick = function()
{
   this.attributes.toggleClass("collapsed");
}

with the markup:

<div id="sidebarHeader">
  <span class="icon-horizontal">...</span>
  ...
</div>

And styles:

.icon-horizontal {  } /* normal styles */
.icon-horizontal.collapsed {  } /* collapsed styles */

The reasoning above is not just Sciter specific but is also valid for conventional browsers.

Lightweight inline vector images in Sciter.

I think all web UI designers time to time experience need of drawing of non-trivial shapes directly in CSS.

Lack of such feature leads to appearance of far from being optimal solutions: icon fonts (hi Awesome), crazy attempts to draw non-trivial shapes using CSS border artifacts and excessive use of additional pseudo-elements: `::after` and `::before`. People are writing tons of essays on the subject. See how far it can go. Phew…

Thus I’ve decided to add inline vector images to Sciter.

Consider this SVG element:

 <path d="c 50,0 50,100 100,100 c 50,0 50,-100 100,-100" fill="#000" />

Note that d attribute value. It defines path and use pretty simple yet powerful format. But just to draw that simple curve SVG requires bunch of DOM elements to be created around it. Not that lightweight for that simple purpose.

And here is what I did in Sciter: it now supports so called path urls that use exactly that svg::d format:

div {
  background-image: url(path:c 50,0 50,100 100,100 c 50,0 50,-100 100,-100);
  background-repeat: no-repeat;
}

Note that url above, it is just a content of that d attribute, just prepended by “path:” part.

Here is an illustration of such inline vector images:

inline-vector

And so you can draw anything in CSS now, any shapes and icons you want.
As a bonus: such images can be created in any visual SVG editor like Inkscape or Adobe Illustrator. It will require manual copy paste from SVG source into CSS but still.

Landed in revision #3955 for upcoming v.3.3.1.12

Adding inline C functions to Sciter :)

As a response to WebAssembly initiative I am thinking about adding native inline C functions to Sciter.

// that is inline C function declared directly in script:

function getAverageColor( image ) C {
   typedef struct {
     byte r;
     byte g;
     byte b;
     byte a;
   } rgba;

   rgba*    pix   = image_pixels(image);
   unsigned total = image_pixels_count(image);  
   rgba*    pix_end  = pix + total;

   unsigned int r = 0, g = 0, b = 0, a = 0;
   
   for(; pix < pix_end; ++pix )  {     
     r += pix->r;   
     g += pix->g;
     b += pix->b;
     a += pix->a;
   }

   rgba out;
   out.r = r / total;
   out.g = g / total;
   out.b = b / total;
   out.a = a / total;
   return out;   
}

And so we can call that function in script when we will need to get maximum performance:

var img = new Image(w,h,element);
var averageColor = getAverageColor(img);

Such functions will be translated on the fly into native code and so executed as fast as possible.

Huh?

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) .

CSS: vw/vh/vmin/vmax units are quite dangerous

Consider the following markup:

<section style="width:min-content">
  <div style="width:10vh">
</section>

Note that the div has width equal to 10% of window’s height.

That looks quite simple but introduces quite unpleasant problem.

HTML/CSS layout task consists of two major steps:

  1. Layout width -> will give us min/max widths and content height, quite expensive as almost always it is O(N) complex.
  2. Do layout height as a final step. That’s essentially quite cheap vertical alignment operation and only where it is needed.

When you change height of the window (but not width) you can skip step 1 completely.  Even while changing width many elements will keep their min/max widths intact. That allows to save CPU cycles quite a lot.

But width:10vh is a game changer here – it ruins such optimizations completely. Even when changing only height of the view you shall do full relayout 🙁 The same applies to vmin/vmax units.

Use vh/vmin/vmax units responsibly.

::mark(…) feature is comming

Please consider these tasks:

  1. Find all words in text on HTML page and highlight them
  2. Syntax highlighting: parse text of <pre> and mark all keywords by changing their color
  3. Find all misspelled words in <textarea> highlight them specifically.

Currently you can do #1 and #2 by wrapping each text found into <span>s with specific classes – thus to modify DOM structure of the document. Such modification by itself is a) not trivial to implement, b) is highly non-desirable (consider text editor with syntax highlighting) and c) even impossible in case of <textarea> for example.

Ideally we should have some facility that will allow us to a) mark text fragments, b) apply styling to such marked runs through CSS and c) do that without DOM tree modifications.

As a result upcoming Sciter will have:

  1. In CSS, support of ::mark(...name[s]...) pseudo element.
  2. In script runtime, new methods of Selection class:
    • Selection.applyMark(start,end, ... mark names...); – to add marks to the range.
    • Selection.clearMark(start,end, ... mark names...); – to clear marks from the range.

Example, CSS:

div::mark(hilite) { background-color:lime; } 
div::mark(hilite2) { background-color:cyan; }
div::mark(literal,string) { font-style:italic; color: red; }

First rule will tell the engine to set background color on all text runs that are marked by “hilite” to “lime”.

And script code

      var text = $(div).firstNode;
      var start = [bookmark: text, 6, false];
      var end   = [bookmark: text, 11, false];
      Selection.applyMark(start,end,"hilite");

will apply mark “hilite” to the text node from 6th until 11th position and so you will have the following rendering:
marks in text nodes
Note: this will not change DOM structure but rather apply some meta information to characters in the text.