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

Sciter with Skia backend, beta

Here is experimental build of Sciter with Skia backend.

sciter.com/sdk/sciter-sdk-3-skia.zip

The version is 100% API compatible with previous builds – drop-in replacement.

Folders:

  • /bin/ – Windows binaries, Direct2D and GDI+ backends.
  • /bin.skia/ – Windows binaries, Direct2D and Skia backends.
  • /bin.osx/ – OSX binaries, CoreGraphics and Skia backends.

All Windows samples and Sciter itself are compiled with MSVC 2015