+plus and +formation, what’s the difference?

+plus

+plus is the way to define and support mapping between DOM tree and data tree rooted at some object or namespace. So if you have these two declarations, markup:

<style>@import url(plus.css)</style>
<section model="person">
    <label>First</label> <input(name.first)>
    <label>Second</label> <input(name.last)>
    <label>Age</label> <input|integer(age)>
</section>

and script

namespace person {
  var name = { first: "Albert", last:"Einshtein" };
  var age = 53;
}

then the +plus will establish live two-way mapping (binding)  between DOM elements above and the data structure.

When the user changes value of input(name.first) field  (short form of <input name="name.first"> in Sciter) the following happens:

  1. Sciter generates “change” DOM event;
  2. +plus handles that event and
  3. updates value of person.name.first in data namespace.

And when data gets changed by some other code like

person.name.first = "Some other name";

then pretty much similar flow occurs:

  1. Sciter determines that filed first  has changed on observable object.
  2. Sciter calls attached function-observer – each object/array inside bound namespace has an observer function attached to it.
  3. And that function-observer updates bound DOM element.

As you see, in order binding to work both-ways, each data node (object, array) in bound namespace has to have corresponding observer attached to it.

+formation

The main difference from the above is that the +formation does not require separate data structure.  The formation is a data structure by itself naturally mapped to markup structure.

This markup:

<section(person)>
    <label>First</label> <input(name.first)>
    <label>Second</label> <input(name.last)>
    <label>Age</label> <input|integer(age)>
</section

gets mapped by formation() function to the [formation] tree structure:

And code can access that tree directly as:

var root = formation( self );
// setting whole formation:
root.person.value = {
   name : {
     first: "Albert",
     last: "Einshtein"
   },
   age:69
 };

// or setting particular field in the formation:
root.person.name.first.value = "Some other name";

Note: root.person.name.first is a direct reference to <input> DOM element thus .value is required.

The formation is a data structure derived from actual DOM tree (its projection)  and so you can use normal DOM events to be notified when some data changes by the user:

root.person.name.first.on("change", function() {...}) // or
self.on("change", "section[name=person]", function() {...}) // any change in person fields.

Resume

Essentially +plus and +formation are aimed to the same task – update UI from code and update/notify code about some changes in UI.

  • +plus
    • pros: Allows to bind arbitrary data structure to arbitrary DOM tree.
    • cons: Can be memory and CPU consuming on large data and DOM trees.
  • +formation
    • pros: Fast and not CPU consuming.  Yet allows to access “fields of interest” by using dot notation (“paths in formation”):  root.person.name.first.state.disabled = true;
    • cons: data structure you get (the formation tree) is bound with DOM structure. So when you change DOM structure then  formation paths in code may change.

Better use of style @set’s

In next version (4.0.0.2 and above) I am changing the way of how @set’s are applied in user’s style sheet.

Style sets will be applied before any other rules in the same style sheet.

So if you have

@set MySet {
  :root { color:red; width:*; }
}

myelement { style-set: MySet; }

And later define

myelement#test {
  color:blue;
}

then <myelement id=test>...</myelement> element will have blue color.

So you can think of style sets as definition of default styles. Ordinary CSS rules are applied on top of them allowing to style concrete elements.

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.

CSS variables support

I am adding support of CSS variables. In Sciter this is pretty much about the same feature set as defined in CSS Variables Working Draft but with slightly different syntax:

Declaration of the variable:

body {
   var(foo): #f00;       /* declaring variable with name "foo" 
                            having color value */
   color: var(foo,#000); /* using the variable */
}

Variables are inheritable so everything inside <body> can use that variable:

div {
   background-color: var(foo,#00f);
   size:200dip;
}

Note that variable usage contains two pieces: name of the variable and default value that will be used when variable is not defined.

DOM API in script will also get methods to get/set variables in runtime:

$(body).style.variables { 
    foo: color(0,255,0) 
}

After that all elements that use the variable in their styles will get new color.

“collapsed” and “expanded” state flags in Sciter

Concept of collapsed/expanded sections (panels, fragments, etc.) is so widely used in UI that I’ve decided to add them to the core.

So there are two general purpose UI state flags that are accessible from script for read and write:

element.state.collapsed = true /*or false*/;
element.state.expanded = true /*or false*/;

These states are related to each other – the element can be in either collapsed or expanded state, but not in both. When you say element.state.expanded = true; then element.state.collapsed will be set to false. And vice versa. By default element is in neither state – neutral in this respect – not collapsed and not expanded.

In CSS these state are reflected in corresponding state pseudo-classes:

element:collapsed { ... }
element:expanded { ... }

In different scenarios collapsed or expanded state means different things. In some cases collapsed element means element is hidden / invisible as whole by defining in CSS:

element:collapsed { visibility:none; }

In other cases, like section with caption and content, the collapsed state means “hide content but show header always”

<section.collapsible>
 <header>section header</header>
 <div>
 ... content ...
 </div>
</section>

In this case CSS may contain this visibility rule:

section.collapsible:collapsed > div { visibility:none; }

So when section is in collapsed state its content div is hidden completely. And it could be other rules that define look of collapsed state, like +/- icons on the header, etc.

In script you can put simple trigger that expands/collapses sections with collapsible class:

self.on("click", "section.collapsible > header", function(evt) {
  // click on header
  var section = this.parent;
  // flip-flop collapsed state:
  section.state.collapsed = !section.state.collapsed; 
});

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