Posts tagged with 'TIScript'
4 years ago

CTRL+SPACE programming in TIScript


CTRL+Space programming is how I consider the crazy way a programmer ends up coding, specifically when working with a statically typed language (like C++, C#, Java) and a good IDE.

Essentially it is all about autocompletion, about having the IDE telling what is available in the context where you are typing:

  • telling you the available names, like variables, classes, methods, ...
  • telling you the available APIs methods so you don't have to remember nor consult the docs
  • letting you use code snippets
  • autocompleting when there is only a single option available when you press CTRL+Space

So the benefits of autocompletion are many, so I don't think I need to explain the importance of it for beeing a productive programmer.

TIScript autocompletion

Well, TIScript is a dynamic typed language, so you can't know the type of a variable with static analysis. That was a big challenge I faced when developing OmniCode and it's Intellisense support.

I had to resort to some smart ways to discover the type of variables and calls for known API methods so I can show you their signature and needed parameters.

There are 3 methods you should know that OmniCode uses to give you autocompletion where possible:

1 - Naming conventions

OmniCode infers the type of a given variable based on its name. That is, you programmer, by following a convention in naming your variables, is helping OmniCode to know the type of variables.

As in the following GIF, by naming the variable as el_div, it is inferred its type as Element, because of the el_ prefix.

Notice that identifiers with inferred type have a distinctive grayed color.

This way, when you type a dot, OmniCode can show the methods/properties available for Element class, or for whatever type which was inferred, as Graphic for variables named gfx.

All the rules for these naming conventions are available here: conventions table.

2 - Dot stack analysis

When you are typing the following code, OmniCode correctly shows the completion-list of methods/properties available:

The following analysis is made:

  • Statement is splitted by the . separator
  • self#menu-cfg is automatically inferred as Element type (it's a way in TISript that to get an Element reference by its ID)
  • $ is a valid method of the Element class and it returns an Element
  • Knowing the returned type is Element, we know the names available, and as you already started typing ht, the list is filtered to show only names containing ht

All this is possible because OmniCode contains a database of all TIScript built-in API, that is, all methods/properties/constants of classes/types you find in Sciter documentation.

So with this analysis of statements, you have not just autocompletion lists, but all features of a good Intellisense experience:

Signature help - shows method prototype (and overrides) and highlights the current argument being typed

QuickInfo - show details about the name under cursor

3 - Semantic analysis

OmniCode keeps track, at real-time, of all user-defined symbols/names, so you get autocompletion for your own code:

That is, after you type any TIScript code, the file is parsed, and declarations of variables, consts, functions, classes and namespaces are extracted.

import "xyz.tis" statements will trigger scanning of the imported file.

This database of locations of every user-defined symbols makes possible the GoToDefinition functionality, that is, allows you to, for example, press F12 in a function call statement and navigate to where the function is defined.

Bonus: self#ID completion

Another clever autocompletion support is presented after you type self#.

It shows all ID available from your HTML markup:

The database of IDs is built from open HTML files. That is, make sure to open all HTML files that you want to have its DOM tree parsed and IDs extracted.

Pressing F12 in a self#myElementID expression opens the location of the tag/element in your HTML markup with the given ID, if it exists.

Next post I will show how do CTRL+Space programming in CSS, stay tunned.

4 years ago

Sciter event handling methods explained


Events in Sciter happens mainly from user interaction, be it over mouse or keyboard input.

But they can also come from other sources. For example, you can trigger them programatically, or the engine itself might trigger events related to the UI, like when a CSS animation ends/starts, or when the DOM is changed.

Whatever events you are interested, you must use a method to handle then.

This post will describe the many coding approaches that you can use in TIScript to handle events.

Event sinking/capturing + bubbling

Before starting, it is fundamental for you to know what is event sinking/capturing + bubbling, which is how generated events gets prograpaged throughout the DOM hierarchy.

It is the same mechanism found in modern browsers Javascript support, so if you already know about it, Sciter follows the same model.

If you don't know this mechanism, do read this article:

In the end you must know that in Sciter:

  • event first has a sinking/capture phase, where it goes down the DOM hierarchy
  • then the event has a bubbling phase, where it goes up the DOM hierarchy, that is, it bubbles up

Notice that Sciter documentation uses the term sinking, and not capturing, it is all about the same thing.

In standard browsers, if you want to handle events in the capture phase, you must to use addEventListener() with last argument set to true, so it is the only way to catch events at capturing phase.

I think that in Sciter, TIScript syntax allows you to more naturally choose wheter you want to handle an event in its sinking or bubble phase, as I will describe bellow.

Non-bubbling events

Also it's fundamental to know that some events don't have this sinking + bubble phase.

They simply go directly to the target DOM element. Events of this kind are called Non-bubbling events.

That is, only the target element can handle the event. For example, drawing events are Non-bubbling, only the target element can draw itself.

So let's get to know how in code you are supposed to declare event handlers.

There are 4 methods, but you usually only use the first two since they are wrapers around the more general and complex call to Element.subscribe().

1 - Method: Element.on()

Sciter supports event handler assignment as in jQuery style:

myElement.on("event", "selector", function(evt) {...});

The on() method assigns the handler function to the particular event that may occur on the DOM element (myElement) or on one of its children.

You can filter for what children you want to handle the event using the selector string which is a CSS selector.

To subscribe to a event in EVENT_SINKING phase prepend the event name by ~ symbol. For example this handler

container.subscribe("~keydown", function() {...}); 

..will receive the keydown event before its children.

Read the docs of this method here.

2 - Method: element << event whatever notation

This is a syntatical-sugar way to declare a function and at the same time attach it as event handler of the element.

Syntax of this event subscription notations is

element << event eventname { ... } or
element + event eventname { ... }

So, for example, to handle the click event on the element with ID "myButton":

$(#myButton) << event click() {

If you need to specify a CSS selector and also want the event parameters, the full form would be:

$(#myButton) << event click $(selector) (params) {
3 - Method: Element.subscribe()

The subscribe() method is the low-level way to handle events, it ends up being a method to handle all kind of events, some which you are not able to handle with Element.on().

Note that there are two signatures of Element.subscribe(). One is equivalent to Element.on(), and the other is the one we are interested:

subscribe( handler: function, eventGroup : int [, eventType: int] ) : <this element>

Internally, Sciter treat events by numbers, not strings. Element.on actually maps the string event name to the internal number.

Notice how we specify the event we want to handle with two integers parameters. These integer are enumerated in the Event class, see here the values.

First is the event group, like Event.MOUSE. Second, you can optionally specify the exact event type you want to handle, like Event.MOUSE_DOWN.

The event type is an ORed integer that exactly identifies the event, its phase and if it was HANDLED when in bubbling phase. That is, it can be ORed with the event type and a flag identifing if on the sinking phase, or if was HANDLED. See Event flags for usage samples.

To handle MOUSE_WHEEL events for example, you must use Element.subscribe because I won't find a event string name to use with Element.on().

I use the following code in my Trello remake to make the mouse wheel to move the horizontal scrollbar instead of the vertical scrollbar.

$(body).subscribe(function(evt) {
	this.scrollTo(this.scroll(#left)-10*evt.wheelDelta, 0);
}, Event.MOUSE, Event.MOUSE_WHEEL);
4 - Shortcut event handlers (legacy/deprecated)

There are pre-defined functions names that you assign to the element that are called for handling events, see the list in the Element class, Element events section.

myElement.onClick = function() {

Problem is, you can only set a single function handler to the onClick property. That's why this method is deprecated, but still usable.

Non-bubbling event handlers

These are events that only the target element can handle, that is, they don't pass though the DOM parents, till the child/target element as sinking/bubbling events does.

mouseenter/mouseleave are example of non-bubbling events. I didn't found a list of Non-bubbling event, but do know that they exist.

Also, I consider Non-bubbling event handlers the paintXYZ properties that you can assign to an element, for example:

myElement.paintForeground = function(gfx) {

Ok, that was enough. I know it seems complex at first sight, but it gets trivial once you know the details.