Modular and extensible UI framework and ui elements.

Posted on
Page
of 2
/ 2
Next
  • 1 of n

    A while ago I played around with touch screen displays that resulted in a quite terse, resource frugal UI implementation: UI Module w/ Buttons, Checkboxes,... for Display w/ Touchscreen.

    Recently I picked up where left of and completed what I was originally going for. Several serious refactoring cycles lead to the 'thing' I'd like to share, to contribute as generally available Espruino modules, and also to provide some tutorials to introduce the rich options... last but not least an introduction how to build and integrate your own ui element extensions.

    Already too many words, therefore a picture right away... actually more than one... See image attachments showing Buttons, Check Boxes, Radio Buttons, Sliders and even a soft '101'-Key keyboard, all on a touchscreen LCD (...which you get for a few bucks).

    More out of a need or constraint than intentionally, I developed a simple cross development environment in the Web browser - after all, it is with display, and the browser has what is needed: display AND JavaScript. The browser gets the modules from the same sand box folder as the Espruino Web IDE and thus validation in the real environment is just an upload click away... (...is as close as an upload click... on the same screen, just in different window). I'll share crosses development as well. At this point it is developed to the point to support this particular ui framework development work, but it has the core ingredients - such as the cache - that allows to add emulating modules for many hard ware modules to run complete applications.


    3 Attachments

    • ui_displayR.jpg
    • ui_espruinoCrossDevRuntie.png
    • ui_displayWithBoardR.jpg
  • 2 of n

    To provide a first glimps into the components, here some code facts:

    • ui.js uses 247 variables for code and data.
    • uiBtn.js uses 90 variables for code, and about 25 variables for each instance.

    The numbers for the slider ui element have not exactly settled yet, but are around the double of a plain button... considering the capabilites, still a very small foot print...

    ...and some code:

    // ----- pull in dispaly and touchscreen
    var dsp, dMod = require("ILI9341"),
        touch, tMod = require("XPT2046");
    
    // ----- pull in ui base and ui element
    var ui = require("ui")
          .add(require("uiBtn"))
          ;
    
    // ----- define callbacks (to log ui element id and value)
    var cb = function(id,v) { console.log(id + ": " + v); };
     
    // ----- define UI (in level 0, so it does not use memory for source)
    
    //   0  1     2       3  4     5    6    7   8
    // flgs clazz id      x  y     w    h   bc  fc
    //      btn                 ->x2 ->y2         
    ui.c(3,"btn","b1"  ,  5, 40,  65,  35,  4,   4
    
    //                    9       10  11 
    //                    valObj  cb,  l (label array obj)
    //                                 fv tc    x  y  label text
                       , "B_1",   cb,  [15, 7,  13, 9, "RED"  ]);
                       
    ui.c(3,"btn","b2"  , 70, 40,  65,  35,  5,   6
                       , {v:1},   cb,  [15, 0,   9, 9, "Y-w-B"]);
    
    // ----- run UI
    function onInit() {
      SPI1.setup({sck:B3, miso:B4, mosi:B5, baud: 1000000});
                       // spi, dc, cs, rst, callback
      dsp = dMod.connect(SPI1, B7, B6, A8, function() {
          dsp.clear(); A5.set(); // display clear and back light on
          // connect ui to dsp and display it
          ui.connect(dsp)
            .w(0,0,239,319) // wipe screen w/ background color
            .d() // display all elements
            .di=true; // set display changes to immediate
          // setup touchscreen and connect it to ui
          SPI2.setup({sck:B13, miso:B14, mosi:B15, baud: 2000000});
          touch = tMod.connect(
              // spi, cs, irq, callback, calc (calibration calc function)
              SPI2, B10, B1, function(x,y) { ui.evt(x,y);
              }, function(yr, xr, d) {
                return [ Math.round(xr / -121.44          + 259.70685111989) 
                       , Math.round(yr /   88.90357142857 + -19.78130398103)
                       ];
          });
          if (touch._eRegisterCanvas) { // only in emulation and before listen
              touch._eRegisterCanvas("dspCanvas", 10, 10); }
      });
      if (dsp._eRegisterCanvas) { // only in emulation dev and before drawing
          dsp._eRegisterCanvas("dspCanvas", 240, 320); }
    }
    
    onInit(); // development convenience only... not part of final, saved application code.
    

    The onInit() invocation - last line in the code - does not really belong to the application code... but while developing, it is is very convenient, because the system behaves the same way as with saved code and power-cycling but without actually having to save the code and power-cycling... ;-)

    To understand touch screen related lines 45..47, take a look at recent Touchscreen conversation.

    Lines 49..50 and 52..53 are cross development environment related and were a quick (and dirty) compromise to keep the emulating modules application independent... These lines do not belong into Espruino, and that's why they are conditioned and therefore do no harm...

    As in above paragraph, emulation shows... but really marginal... and with some smart thinking - or (with some not so beloved) conventions - emulation can become completely transparent.

    The code shows how the Espruino code is prefixed in order to run in cross development. Below few javascript lines are the only entries required to precede the Espruino code to run it in an html document:

    <script src="_emulationConfig.js"></script>
    
    <script>function _eeLoad(eComponentName) {
      document.writeln(['<scri','pt src="',_eEmulationPath,eComponentName,'.­js"></scri','pt>'].join("")); }
    </script>            
    
    <script>_eeLoad("_eEspruinoJsShim");</sc­ript>
    <script>_eeLoad("_eEspruinoPicoShim");</­script>
    
    <script> // (pre) load emulating modules - emulating code upload to board
    _eeLoad("ILI9341"   );
    _eeLoad("TouchRD");
    </script>
    
    <script> // (pre) load (non emulating) modules - emulating code upload to board
    _emLoad("ui");    // ui base (code and runtime data holder)
    _emLoad("uiBtn"); // uiBtn (Button)      element
    </script>
    
    

    As obvious, a plain html file is used. To avoid a Web server, it is all pulled with just file:// protocol from directories which are configured in line 1 - pulled in as well from a java script fragment while the page is loaded... same concept as files are uploaded into Espruino, they get evaluated... and so does the html with embedded code when pulled in by the browser.

    Lines 3..5 show this loader that adds javascript on the go, as you see in lines

    • 7..8: Espruino ecology with emulated board
    • 11..12: Emulated display and touchscreen modules
    • 16..17: ui base and uiBtn ui element modules

    I was surprised how well the UI behaves regarding speed - even though the communication with the display is serial... Text (drawString()) still drags its feet - understandably - but all other things are extremely snappy, such as visual touch feedback. I guess it is thanks to @JumJum who recently made some speed enhancement when using the Graphics objects - the 'inner' implementing of the LCD displays...

  • Have you posted the module somewhere accessible?

    Thanks,

  • ...got a bit stuck in doc and work... will show in public in a bit...

  • @user54159 , check your forum messages...

  • @allObjects Thanks!!!!

  • uiReadMe.txt

    Hi,

    The modular and extensible ui framework for Espruino from allObjects - is now ready for use 'as is' (finishing touches still ongoing).

    6 folders - _sbx (an espruino sandbox ready to be used), _ecomponents, uidev, edev, uidoc and uixsrcmaterial - and this post as text in ui.zip file fort download below.

    Unzip them into any directory. Make _sbx the sandbox folder in Espruino Web IDE. Upload the (example) projects to run them on your Espruino board.

    Not the right hardware (yet) at hand? or: Not much time either for a sneak peek? - No problem:

    Look at and play around with the very same examples in your Browser...

    To run / develop the same modules and projects in emulation / cross development mode in Browser: open .html files - uiExample.html - in uidev folder and click link all.

    uxscrmaterial folder contains many useful examples and older, simpler versions of the ui, most of them still working. Some of them will migrate over time into the actual modules and projects folders.

    Goal is to harden the modules, grow the example pool, add examples for various hardware, also hardware without touch screen support, and make it all available on the Espruino site - as modules and as tutorial(s).

    best js hacks - @allObjects


    Attached: shot showing zip file content, ui_20190927.zip (zipped ui folder w/ all you need for ui). - ui_20191119.zip - newer version (still in work)


    3 Attachments

  • ----- ui_ ------------------------- 20190927 - intro

    *** modular and extensible (graphical) ui framework - regular version.

    The framework consists of a bases module which provides the infrastructure
    for individual out of box and custom ui elements and utility extensions.
    Out of box ui elements include (for now):

    • plain button
    • check box
    • radio button
    • slider
    • input field
    • keyboard (soft keyboard / touch keyboard)

    An out of box extension provides utility and convenience functions and
    is required for more complex ui elements - for now - for - out of box -
    radio button, and is also useful for the application to easy access
    ui elements.

    Creating and integrating custom ui elements and extensions as needed
    is easy based on the architecture and available documentation and
    ready to run examples. Examples can be run on actual Espruino and
    display with and - soon also - without touch screen. Most of the
    examples run also emulated in cross development environment in any browser
    as html sharing the Espruino JavaScript files - that's how and where
    the ui framework and ui elements are developed and maintained for most
    of their functionality.

    All ui elements handled by ui framework have roughly 12 (13) common
    properties in same and similar structure(s):

    • flags - hidden/displayed, touch active, read/display-only, in focus,...
    • clazz - btn-Button, chk-Checkbox, rad-Radiobutton, sli-Slider, inp-Inputfield,...
    • id of ui element - enabling sharing callbacks and other nifty things,
      such as keeping additional, application specific state and behavior
      the ui framework does not cater for (yet) or will never do or can...
    • x and y position - left-top of ui elements bounding box
    • width and height
    • border and fill colors
    • a value (object) enabling sharing callbacks and other nifty things
    • a preferred callback - fired on untouch after touch down on same ui element
    • some label object(s) - plain, static text but also as renders (functions)
    • (optional, NON-preferred, experimental callback called on all touch events)

    Only the 1st 9 of the 12 (13) properties are really mandatory. Same
    values in same structures have lead to an implementation of the ui
    elements as light weight, lean 'array objects' in order to save memory
    / variables.

    Details about the ui element specific properties for construction and
    runtime are documented for each ui element separately.

    The ui communicates input (and other) events through callbacks. The
    callback arguments include all pertinent information to allow a straight
    forward, from simple to sophisticated use in the application and keep the
    code terse in order to save memory / variables:

    • id of ui element
    • value (object) of ui element
    • ui singleton (module) for accessing all statuses / ui elements / ui aspects
    • ui element subject to touch event (for optional query/change by application)
    • touch event object (touch/untouch and position on display / ui element, flags)

    Attached shot: ui start up w/ all ui element types and keyboard (in emulation).


    1 Attachment

    • startupExample.png
  • ----- ui_ ------------------------- 20190919 - intro

    *** ui framework - base module.

    The ui base module takes care of most of the plumbing, logic and data
    flow between touch screen (or physical buttons) and display as input and
    output components and controling application.

    --- Implementation approach

    The ui base module is currently implemented as singleton, literally constructed
    when loaded (required). At runtime it does not only hold on to its own code and
    data but also the mixed in code for the various ui element 'types' as
    needed (as loaded on demand by require() ) and the data of the
    created ui elements. The ui elements are implemented a light weight array
    objects: just data and - where needed - some format function(s).

    --- Memory / Variable usage

    Below some (ballpark) figures on various usage, categorized as U
    for unminified vs. M for minified and I for individual vs C for cumulated.

    // vars (Idividual|Cumulated)  IU /  IM -   CU -   CM  B (Un|Minified)
    var ui = require("ui")     // 314 / 282 -  361 -  302 20       (-Base)
        .add(require("uiExt")) // 237 / 214 -  598 -  515
        .add(require("uiRad")) // 190 / 168 -  788 -  683
        ;
    var ui = require("ui")     // 314 / 282 -  361 -  302 20
        .add(require("uiBtn"))
        .add(require("uiChk"))
        .add(require("uiExt")) // 237 / 214 -  598 -  515
        .add(require("uiRad")) // 190 / 168 -  788 -  683
        ;
    

    Minification setting in Espruino Web IDE: Esprima (offline) for both code
    in Editor window and from modules.

    *** Basic Application of ui base and - as example - use of uiBtn (Button) element

    Get ui base and ui elements and build ui - like a DOM - by ui definitions in
    level 0. The build is executed on upload to Espruino. save() will save
    the built ui. The onInit() function will start the already built ui
    on power up, after save() or - while under development - manually by
    invocation in console or automatically in a setTimeout() (see below).

    --- Require ui components

    Get ui base and ui elements to the extent as needed into the system,
    such as uiBtn, uiSli(der), etc.

    var ui = require("ui")     // getting ui base code (w/ 8 default colors)
        .add(require("uiBtn")) // add / mixin btn ui elt support into ui
        ;
    

    ----- Define UI and add elements

    Definition of UI(s) - creation of 'DOM'(s) - best happen (with Espruino)
    on code upload and is/are as such then saved on save(), but not displayed
    (rendered) yet. Creating it dynamically is possible as well. Example UI
    below defines ui with two (2) buttons -b1 and b2 - which on
    release (untouch, tap) will call the example callback `cb. Button ui
    element definition includes just data and requires out of box no format
    functions. For more details see uiBtn module (unminified code) and related
    documentation.

    //    0 1      2      3   4   5   6   7   8  9      10  11 
    // flas clazz  id     x   y   w   h  bc  fc  valObj cb,  l (label array obj)
    //      btn                ->x2->y2                        fv tc    x  y   text
    ui.c( 3,"btn","b1" ,  5, 40, 65, 35,  4,  4, "B_1", cb,  [15, 7,  13, 9, "RED"  ]);
    ui.c( 3,"btn","b2" , 70, 40, 65, 35,  5,  6, {v:1}, cb,  [15, 0,   9, 9, "Y-w-B"]);
    // bc/fc/tc: border/fill/text colors; cb=callback(id, v, ui, e, t): id: button id,
    // such as "b1"; v, ui, e, t provide btn value, ui, elt and touch event runtime
    // data; colors: 3-bit depth coded: 0b### ###=rgb (0=black, 7=white, 4=red,...)
    

    --- Define application stuff

    The application acts on ui events that are communicated thru callbacks.
    This sample callback `cb - for simplicity reason - just logs the
    buttons' id and value (object) in the console.

    function cb(id,v,_,e,t) { console.log(id+": "+v); } // sample callback
    

    --- Do devices stuff

    Device stuff happens on upload - once - and defines the display and the
    touch instance and module variables and sets latter accordingly.

    The ILI9341 display controller used in the examples is a 240 x 320
    pixel, max 16-bit color, TFT display controller. Other controller can be
    used as well with adjusted width and height values and color definitions.

    If the touch screen has a controller, use its module - like XPT2046
    for resistive touch screen (XPT2046 works also for the ADS7843
    controller); otherwise use the module that can handle a resistive touch
    screen directly (TouchRD - from: Touchscreen, Resistive, where membrane
    edge traces are Directly connected to Espruino pins and controlled and
    sensed by Espruino).

    // instance and module variables:
    //
    var dsp,  dspMod = require("ILI9341");   // display 
    
    var touch,touchMod = require("XPT2046"); // touch screen controller
      // or:
    var touch,touchMod = require("TouchRD"); // touch screen w/o controller
    

    --- Define the onInit() function

    Function onInit() {...} gets everything initialized, connected and
    started. The code below shows touch screen controller module for touch
    screen connection (XPT2046 can also be used for equally
    specified ADS7843). Pins used are related to PICO.

    function onInit() { // on power on/save() setting up all from scratch
      // setup and connect display, then ui and input (touch | phys btns)
      SPI2.setup({sck:B13, miso:B14, mosi:B15, baud: 1000000}); // display
      dsp = dspMod.connect(SPI2, B10, B1,  A4, function() { // using...
                      // ...spi,  dc, cs, rst, callback
        dsp.clear(); A1.set();  // display clear and turn back light on
        ui.connect(dsp)         // connect ui to dsp and display it
          .w(0,0,dspW-1,dspH-1) // wipe screen (rect) w/ default / bg color
          .d()                  // display all elements
          .di = true;           // set display changes to immediate
        SPI1.setup({sck:A5, miso:A6, mosi:A7, baud: 1000000}); // touch inp
        touch = touchMod.connect(SPI1, A3,  A2, function(x,y){ ui.evt(x,y);}
                            // ...spi, cs, irq, callback, /. calibrated...
            , function(yr, xr, d) { // ...function, maps touch to x/y of dsp
                return [ Math.round(xr / -121.44          + 259.70685111989)
                       , Math.round(yr /   88.90357142857 + -19.78130398103)
                       ];
        } ).listen(); // for XPT2046 module (ADS7843: not needed/supported)
      } );
    } // /onInit()
    

    --- Start the code after upload

    After uploading the code, enter onInit() in the Espruino IDE console
    to get the code started. For convenience you can add the following line as
    last line in the code, which gets the code automatically going after
    upload has successfully completed. Note though to remove or comment this
    line on the last upload before saving the code with save():

    setTimeout(onInit,999); // for dev; remove before upload for save()
    

    *** Description of (graphical) ui base module and touch controller interface

    --- Functions / methods of ui base module

    The ui base module provides also convenience functions/methods to be
    used in application to deal with ui elements as well as plain graphics. Due to
    ui's extensibility, you can add easily your own ui element or plain function
    extensions as desired (with .add(module); ).

    • .evt(x,y) - main entry point for touch and touch emulating functions (tap
      function in uiExt module). A tap starts with invocation of .evt(x,y)
      with x and y values; subsequent invocations with x and y values
      indicate dragging, and invocation without x and y indicate an un-touch,
      which completes a tap or touch. ui base module keeps track of the
      changes and makes them available to the application thru callbacks
      with the ui singleton - variable ui / _ - and touch event
      object t with detailed flags t.f. Any other 'entries' can
      be used but keeping the states consistent become the burden of the
      application and is not recommended.
    • .iib(x,y,e) - returns true when x and y (of touch) lay in bounding box of
      ui element e
    • .foc(e) - set focus to element e (and takes it away from element that had
      it so far)
    • .blr(e) - blur/unfocus element e (if .blr(), element currently in focus is
      blurred)
    • .w(x,y,w,h,c) - wipe rectangular area at x/y and w(idth)/h(eight) of
      display w/ optional color c; default color is background color (.bc).
    • .c(arguments) generic, single point entry for creating ui elements. Uses
      first arguments[1] as class or type name and concatenates it with "C"
      for specific create entry. To create a custom ui element and
      integrate it (with .add(require("customUiElementModule")) is
      discussed in a separate publication.
    • .d(e) - (re) draw element e, and when just .d(), redraw the whole ui (all ui
      elements). In general, a ui is built on upload without displaying it
      to save variables and displayed on demand or in onInit() to
      re-display on every re-power up or reset. Note ui elements with
      custom (label) renderers / formatters that use ui () or touch
      event states / flags (
      .te.f, t.f), these states / flags in their
      logic, such as the optional value renderer / formatter of the slider,
      this logic needs adjustment, or, the state(s) / flag(s) need to be
      set accordingly, latter though without interfering with their overall\
      life cycle or their dependents (ui components' logic).
    • .clr(c,i) - set color according this.clrs{0](c,i) (customizable) function.
      Default is set for bit coded value for 3-bit (rgb) color depth and
      provides 8 colors: b&000=0=black, b&111=7=white, b&100=4=red, etc.
      this.clrs[0]() can be customized to support any color depth - coded,
      table looked up, or literal. For details see Colors and color
      definitions
      section below.
    • .fnt(fv) - set fontVector (size) ...currently w/ no frills, as opposed to
      .clr(). More font options will be implemented at a later time.
    • .ld(x,y,l(abelInfoArray)) - label draw function; label info array:
      • l[0]: fontVector (size)
      • l[1]: color (see Colors section
      • l[2]: xOffset to x - left of bounding box of label (.drawString(...))
      • l[3]: yOffset to y - top of bounding box of label (.drawString(...))
      • l[4]: label text (used in .drawString(...))
        Note: the reason for x and xOffset - and y and yOffset - is because
        .ld() is used in conjunction with a ui element that has a bounding
        box that provides x and y - usually left-top corner and xOffset
        and yOffset are used to place the label relative to the ui
        element's corner coordinates. For plain, direct use with absolute
        coordinates, either xOffset and yOffset have to be set to 0. For
        reuse of label at same position, it is better to set both x and
        y to 0.
    • .add(module,keep) - extend ui module with ui element and other extensions
      to support modularity. After module code is merged into ui base code,
      module is removed from cache to save variables. For that purpose, the
      module has to know its name in module name property mn. For example,
      for a module onOff.js for switching back light on/off on pin
      B2, exports = { mn:"onOff" , on: function(on) { digitalWrite(B2, on || on===undefined); }
      defines the module code, ui.add(require("onOff")) adds it to the
      ui.js base code in order to be used as ui.on();. Without building
      a file, modules can also be put into cache directly on upload in level
      0 (and served with require() with module name provided as variable to
      escape regular module detection and pre-upload from modules
      sources): Modules.addCached(mName,'{ mn:"onOff",on: function(on) {' + digitalWrite(B2, on || on===undefined); }');, and added
      with ui.add("onOff");.


    --- UI (touch) event object (passed in callbacks)

    The touch event object t and other objects, such as the ui
    / _ and the ui element e themselves, are passed around for easy
    access in the ui base and ui element implementation as well as in the
    application. The touch event object has this structure:

    t = // (touch) event
       { x: x // current or last touched x coordinate
       , y: y // current or last touched y coordinate
       , t: t // touching: (truey), not touching (anymore): falsey
       , f: f // touch event flags (experimental)
       }
    

    Touch event flags t.f - experimental - are:

    0000 0000 0000    0 unexpected (invalid?)
    0000 0000 0001    1 untouch
    0000 0000 0010    2 touching
    0000 0000 0100    4 touch down
    0000 0000 1000    8 'untouch' focused element while not in focus - _.lf
    0000 0001 0000   16 untouched focused elt while in focus (typical untouch)
    0000 0010 0000   32 moved out of focused element (touched down element)
    0000 0100 0000   64 touching in focused elt (touched down elt) - _.ef / _.lf
    0000 1000 0000  128 touching / focused 1st time or re-focused - _.ef / _.lf
    0001 0000 0000  256 untouch on dragged over non-touched-down, alt elt - _.af
    0010 0000 0000  512 moved out of non-touched-down, alternate focused elt
    0100 0000 0000 1024 touching in non-touched-down, alternate focused element
    1000 0000 0000 2048 touching 1st time in non-touched-down, alt. focused elt
    

    Re-focusing happens when having touched down, dragged out of the element
    and now draging back 'into' the initially focused element (touched down
    element (_.ef / _.lf).

    In preferred callback, a typical untouch - touch down on a ui element and
    untouch on same ui element - set these flags:
    conditions:

    0000 0001 0001   7 = 16 untouched focused element while in focus (simple,
                        +  1 untouch event
    

    Untouching after moving / dragging out of the bounding box of the touched
    down ui element sets these flags:

    0000 0001 1001   25 = 16 untouched focused element while in focus (simple,
                             regular untouch event)
                        +  8 'untouch' focused element while not in focus
                             (_.lf) - last registered focused element, now not
                             in focus anymore (_.fe), but may need cleanup
                             or reset when element has been modified on other
                             events using alternative callback
                        +  1 untouch event
    

    In the preferred callback fired on ontouch only - touch started with
    touch down and ended with untouch on same ui element - requires usually
    no or only rarely interpretation of touch event flags.

    The alternative callback fires on every kind of touch event and demands
    significant interpretation of the flags from the application and ui
    status items - passed as well in the callback - in order to take
    appropriate action(s) in the application. The multitude of flags
    allows to determine - next to untouch - touch down, dragging within,
    leaving and re-entering bounding box of touched down ui element as well
    as same events of the alternate - not touched down - element (and can
    be used to implement drag and drop).

    --- Colors and Color Definitions

    ui base module includes a default color converter that supports 3-bit
    color depth - 1 bit each per red, green and blue base color - coded as
    single RGB integer value, yielding 8 colors: 2^1^3 = 8 colors (3 LSB -
    Least Significant Bits of an integer). The color converter is a
    function and is first element - index [0] - of an array which is set
    and stored as ui.clrs[] array property:

    ui.clrs = [ function(c,i){ var v=(i)?c^7:c;
                               return [v>>2&1,v>>1&1,v&1]; }.bind(exports) ]
    

    The default color converter accepts for c a value from 0..7 for the basic
    8 colors and returns them as triplets of 0s and 1s (array of three (3)
    elements of values 0 and 1). It accepts an optional second parameter to
    invert the color when truey. The inverted, complementary color contrasts
    the non-inverted color and is defined by the color specified by the
    1-complement of color c. For example, for a the 3-bit color depth defined
    color with value 4, [1r,0g,0b] or red, the inverted,
    contrasting complementary color value is 3, [0r,1g,1b]
    or aqua.

    The color converter is is invoked by ui's color setting method ui.clr(c,i)
    with same arguments. Any color converter delivers - has to deliver -
    the normalized values 0..1 for each of the RGB colors as
    [r, g, b] triplet, because hat values triplet is parameter for setting the
    color by invoking the set-color method .setColor(r, g, b) of the
    Espruino Graphics object. The display module knows how to pass the color
    onto the display's controller to make the display show the proper color.

    Using the enhanced color converter as below, colors can be specified in
    three ways:

    • bit-coded as single positive value
    • (negative) index into palette of [r,g,b] triplets
    • literal [r,g,b] triplet

    The n custom colors of the custom color palette are stored in the same
    array as the color converter as 2nd to n-th + 1 element - index 1..n. The
    color converter has to be built to accept 'negative' color values -1 .. -n
    to pick a color from the custom palette: it takes the negative value as
    positive index into the ui.clrs[] array property tp pick a color
    from the custom color palette and return it as value-normalized
    [0..1r,0..1g,0..1b] triplet. The way the custom colors of the custom color
    palette are specified determines the implementation of the converter.

    ui.clrs = // 'imply' color depth and populate table w/ converter and define
              // user colors / palette (colors accessed w/ negative index)
      [function(c,i) { // converts 'custom color info / spec' to [r,g,b]...
           var v, s=isNaN(c); // ...@ idx 0; internally called: _.clrs[0](...)
           if (s || c<0) { // c<0 (looked up [R,G,B]) or c=[R,G,B]
             return ( ((s) ? c : this.clrs[-c]).map( // convert 0..255->0.0..1.0
                                   function(v){ return ((i)?v^255:v)/256; } ) );
           } else {        // c>=0 bit coded rgb color (0b001,010,011,100,...111
             v = (i) ? c^7 : c;  // (default 3-bit color depth w/ 2^1^3=8 colors)
             return [v>>2&1,v>>1&1,v&1]; }
         }.bind(ui)  // custom color palette (converter knows to convert spec)
      ,[216,216,216] // user color -1: light grey // keyboard special keys
      ,[192,192,192] // user color -2: medium light grey
      ,[128,128,128] // user color -3: medium gray
      ,[192,128,192] // user color -4: light purple
      ,[0  ,160, 64] // user color -5: kind of a 'darker' green
      ];
    ui.bc = 0; // ui (screen) background color (override default w/ other value)
    ui.tc = 7; // ui (screen) touch / focus color (override default w/ oth. val)
    

    For [r,g,b] specification, Espruino Graphics modules accepts for each
    r,g,b base color the normalized value 0.0 .. 1.0. Under the hood and with
    help of the module for the display, this normalized color specification is
    transformed - most of the time - into a bit coded color again, but in a
    different, more elaborate / complicated encoding, especially when
    different number of bits are used for each of the base r, g and b colors,
    for example, (display specific) 5, 6, 5 bits for r, g, b. The module also
    handles the conversion the sequence of the r, g and b colors when needed,
    for example, [r,g,b] to [r,b,g].

    Normalized color specification value is mapped to actual color based on
    color depth specified for or implied by the display (module) used in
    conjunction with the Espruino's Graphics object. The ui base module
    already includes a default color converter from bit coded rgb value to
    Espruino's normalized [r,g,b] values triplet. Passing the normalized
    values triplets makes bit coded colors in the ui independent from the
    display's version of bit coding.

    The n custom colors of the custom color palette are stored in the same
    array as the color converter as 2nd to n-th + 1 element - index 1..n.
    The color converter has to be built to accept 'negative' color values
    in range of -1 .. -n to pick a color from the custom palette: it takes
    the negative value as positive index into the ui.clrs[] array to
    properly pick a color from the custom color palette and return it
    converted to value-normalized [0.0..1.0r, 0.0..1.0g, 0.0..1.0b] triplet.
    The way the custom colors of the custom color palette are specified
    determines the implementation of the converter.

    --- Custom ui elements and extensions:

    In order to not collide with future development of the ui components
    out of the box and for easy distinction, choose for custom ui elements
    clazz names of 5 characters or more, use for mixed in properties -
    variables and methods - names with 6 or more characters. Also, add a
    custom parallel array object or real js object rather than adding
    elements to the existing array object.

    --- More...

    The unminified sources and examples have in-line documentation - same
    as this - and in-line comments. Latter is useful when reading the code
    to grok 'what is going on'. Feedback - in general and in particular -
    and (improving) contributions are welcome (@ allObjects).

    --- ui - base code into which elements and custom code are mixed in:

    exports = // ui base / 'DOM'/ui e(lement) data & code holder, singleton, for mixins)
    { dsp: null // display (Espruino Graphics object)
    , mn: "ui"  // reserved, used temporary
    , bc: 0     // dsp background color >=0: r*g*b* bit coded; <0: looked up [...,[R,G,B],...]
    , tc: 7     // touch / focus color; >=0: r*g*b* bit coded; <0: looked up [...,[R,G,B],...]
    , di: false // display instantly on create/change (not defer until after save())
    , es: []    // ui elements
    , ef: null  // (primary ui) element (in visual) focus (by touch down / hovering)
    , lf: null  // (primary ui element) last (in visual) focus (by touch down / not hovering)
    , af: null  // alternate (ui element in non-visual) focus (by drag over / hovering)
    , it: false // (display) is touched
    , td: false // is touch down (event)
    , tt: 0     // touch down time
    , lx: 0     // last x (touched)
    , ly: 0     // last y (touched)
    , dx: -1    // (last) x (touch) down
    , dy: -1    // (last) y (touch) down
    , te: {f:0} // (last) touch event
    , clrs:     // default - bit coded color of 3-bit color-depth - 2^1^3(rgb) = 8 colors
    // ....
    // ...  public methods - documented above
    // ... private methods
    // ...
    }
    

    Attached shot: Button b1 and then button b2 'tapped'.


    1 Attachment

    • uiBtnExample.png
  • ----- uiBtn ------------------------- 20190927

    *** ui button module

    The ui button module extends ui base with button ui element.

    Implementation note: vs3 overlaps/is duplicates of uiExt module and
    will overwrite each other. Therefore - on changes - changes have to
    be applied to both of them to maintain consistent outcome. It is
    duplicated in the ui checkbox so that ui extensions does not need to
    be loaded when not used. It saves memory / vars with simple UIs.

    --- Enable ui base with uiBtn and create a button with callback on untouch:

    var ui = require("ui")       // load ui base module
          .add(require("uiBtn")) // add module into base and remove from cache
          ;
    

    --- Create - specify - plain buttons

    // flgs  clazz id   x  y  w  h b f  value object /. callback on 'untouch'
    ui.c( 3,"btn","b1",10,20,50,30,4,7,"B_1"
                      ,function() { (LED1.read()) ? LED1.reset() : LED1.set() }
                      ,[20,0,5,15,"RED"]);
    //                  fv t x y   label text
    

    Creates, adds to ui, conditionally displays and returns an active(2),
    visible(1) =(3) button ("btn") with id "b01". Button is positioned at
    10 @ 20 (left @ top, x @ y) and is sized 50 x 30 (width x height), has
    4(red) / 7(white) border / fill colors, has value object string "B_1",
    has (arguments ignoring) callback that toggles red LED1, and is labeled
    "RED" on top of it in fontVector (size) 20, text (font) color 0 (black
    on white button fill color) and x / y-offset of 5 / 15 from left top
    corner of button's bounding box.

    Colors are bit-coded with 3-bit color-depth according ui.clrs=[...] setup.

    Callback cb is called on untouch with id, v, ui, e, t as arguments:

    args[] sym   description 
      0    id  : button id ("b1")
      1    v   : value object ("B_1" - can be any object)
      2    ui  : ui (THE ui object)
      3    e   : uiBtn element (btn 'object' (runtime data structure))
      4    t   : touch info x, y,...  (where last touched)
            { x : x coordinate
            , y : y coordinate
            , t : touched (truey) / untouched (falsey)
            , f : flags
            }
    

    For ui base, color, label, and callback details see (also) ui base module.

    For detailed ui setup, including color depth and custom colors, connecting
    to display and touch screen, soft key board or buttons, see ui module and
    example material (in the ui.zip file and sources with comments). Take a
    look at the example that shows all ui elements on one single
    display - uiExampleAll.js - project file in the _sbx Espruino sandbox projects
    folder. Make it the Espruino Web IDE sandbox folder and run the ready-made
    examples on your Espruino board. Espruino forum has several entries about
    the modules and hardware. Search in (google) for: Espruino Forum allObjects touch screen display modular ui framework

    No board or display with touch screen yet? No Problem: Open uiExample.html
    in uidev folder in Web browser to run the same example in the Cross
    Development / Emulation environment (where modules are developed and
    maintained).

    --- Some more details

    A button with border and fill colors equal to display background
    color (ui.bc) creates a tap-able area that is useful to make anything,
    such as a displayed image, to act like a button...

    uiBtn can be changed within callback: for example in example above, the
    label can be changed to indicate whether tapping will turn the LED on or
    off. In order to make the ui framework redraw the button and make the
    label change visible, the callback has to return truey ('JS true'). The
    button / callback definition for label changing button looks like this:

    // flgs clazz id   x  y  w  h  b f  value object /. callback on 'untouch'
    ui.c(3,"btn","b01",10,20,50,30,4,7,"B_1"­
                      ,function(id, v, ui, e, t) { // on untouch event
                         if (LED1.read()) {
                           LED1.reset();
                           e[11][4] = "RED on"; // label text
                         } else {
                           LED1.set(); 
                           e[11][4] = "RED off"; // label text
                         }
                         return true; // truey forces button to be re-drawn
                       }
                      ,[20,0,5,15,"RED on"]);
    //                  fv t x y   label text
    

    The same callback shared by multiple uiBtns can produce button specific
    results when callback takes into account the each buttone's unique id or
    value (object) as passed to the callback as 2nd argument. Example code for
    label change above shows that callback parameters include the value object
    of the button (as well as the button element itself). This allows to use
    the same callback with different outcome based on button value object (or
    button's fill color). Multiple, different colored buttons can be used to
    light up an RGB LED (string) in the button's colors as defined by the
    value object (and/or fill a rectangle somewhere on the display with the
    same color as the button's color using one and the same callback to keep
    the code terse and save space (variables).

    Note: The preferred touch event to invoke the callback is on untouch,
    because it has these advantages:

    • UI behavior is simple in sequence and behavior.
    • Enables cancel of touchdown: releasing / untouch AFTER 'moving /
      dragging the touch' out of the bounding box of the initially touched
      button (touched on touch down) will not invoke the untouch callback
      that otherwise would be the inevitable consequence on touchdown on
      the button.
    • Implementation of callback is simpler because it is time-wise less
      critical for delivering a UX behavior with timely, intuitive (visual)
      feedback to user and no delay on execution of the callback.

    To use the (experimental) alternative callback called on all touch events,
    the 11th ([10]) argument value for the preferred callback 11in the
    constructor - ui.c(...); - has to be a falsey value best is
    undefined) and the 13th [12] has to be the callback function. The
    alternative callback is called with the same arguments as the preferred
    one. The touch event object t has 12 bit coded flags - t.f,
    for example for the untouch event 0b000000011001 or 25 -
    which describe the touch event in detail. For details see ui base module.

    Using a synchronous implementation for callback on touch down may delay
    the visual feedback to the user about the accepted touch down when it
    takes noticeable time to complete. Therefore, the callback should invoke
    the heavy lifting code in a setTimeout(function(...){...},10,...);
    function as the last thing in the (otherwise 'empty') callback. The 10
    millisecond timeout is a value to start with. If it is not working, find
    the best value empirically by working thru the application varying the
    value.

    If the button has to change to give additional visual feedback to the user
    that the callback is in execution, then that change has to be coded
    BEFORE* the setTimeout() and the callback has to return truey in
    order to trigger the redrawing of the button in the ui base module.
    Changing the button again after completion of the heavy lifting
    of the callback, it has to happen as last thing IN
    the setTimeout(function(...){...; e[#]=...; ... _.d(e); },10,...);
    (with _ being the ui singleton (ui base module) object, passed
    as the 3rd argument to the callback).

    To prevent additional touch down events on the button to prevent
    triggering the callback again before its completion, disabling of the
    button has to happen before setTimeout() as well
    with e[1]=e|1]|2-2;. Re-enabling as absolutely last item IN
    the setTimeout(){...; e[1]=|2; } code, even after the redraw (see
    above). A more radical approach is to control the touch listener if it
    supports the method touch.listen(false) (and the opposite,
    touch.listen(), which is the same as touch.listen(true)... or 0 and 1
    for false and true argument values, respective). Doing latter though,
    you loose the ability to issue a cancel of a long running callback
    (which of course has to yield once in a while to the touch controller
    module using chained setTimeout()s until done.

    Example of a button with both untouch and touchdown event callback:

    // flgs clazz id   x  y  w  h  b f  value object
    ui.c(3,"btn","b01",10,20,50,30,4,7,"B_1"­
                      ,undefined
                      ,[20,0,5,15,"RED"]
    //                  fv t x y   label text
                      ,function(id,v,_,e,t){ console.log(id+" "+t.f); } );
    

    If you use both callbacks and put 'the (heavy) work' of the touch down
    callback into a setTimeout(function(...){...},10,...);, you may
    consider to put 'the work' of both callbacks into a setTimeout();
    (with equal timeout value) to ensure proper sequence (to prevent touchdown
    callback being triggered first...). Having 'the work' of the untouch in
    a setTimout();, it is recommended to implement button changes the same
    way as it has to be done in the touchdown callback by invoking the redraw
    in the callback with _.d(e); and let the callback return nothing
    (which is the same as falsey).

    --- btn ui element constructor arguments (a[]) and runtime data structure (e[]) are:

    arg runtime 'object' instance of 'clazz' button
    a[]  e[]
     0   [0] f  - flags focus(4), active(2), visible(1)
     .    .         0bxx1 visible &1 visible 
     .    .         0bx1x active  &2 active / senses touches vs read/display-only
     .    .         0b1xx focus   &4 focus by touch down, drag w/in bounding box
     1   [1] c  - clazz "btn"
     2   [2] i  - id eg "b01", short, at least 2..3 chars,  ui globally unique.
                  Single letter ui element ids are 'reserved' (for keyboard(s)).
     3   [3] x  - x ((left ) of focus / touch bounding box)
     4   [4] y  - y ((top  ) of focus / touch bounding box)
     5       w  - width (of focus / touch box)
         [5] x2 - x ((right) of focus / touch bounding box: x - w + 1)
     6       h  - height (of focus / touch box,...
         [6] y2 - y ((bot  ) of focus / touch bounding box: y - h + 1)
     7   [7] bc - border color
     8   [8] fc - fill color
     9   [9] v  - value - any object, from simple string to number to
                  complex { } object (returned when button pressed) 
    10  [10] cb - simple, preferred callback on untouch after touchdown
    11  [11] l  - label (info), array with:
          l[0]  fv - fontVector (size)
          l[1]  tc - (label) text color
          l[2]  x  - x offset from focus box x ( bounding box left )
          l[3]  y  - y offset from focus box y ( bounding box top  )
          l[4]  tx - label text to display (using .drawString())
    12  [12] ca - NON-preferred, experimental callback on any touch event
    

    Attached shot: uiBtn - button - example (in emulation) - touching down on b1, dragging over b2 and back to b1 and then untouch b1 = tapping b1.


    1 Attachment

    • uiBtn_b1_touch_drag_over_b2_back_b1_untouch_tap_b1.png
  • ----- uiChk ------------------------- 20190927

    *** ui checkbox module

    The ui checkbox module extends ui base with checkbox ui element.

    Implementation note: vs2 overlaps/is duplicates of uiExt module and
    uwill overwrite each other. Therefore - on changes - changes have to
    be applied to both of them to maintain consistent outcome. It is
    duplicated in the ui checkbox so that ui extensions does not need to
    be loaded when not used. It saves memory / vars with simple UIs.

    --- Enable ui base with uiChk and create a checkbox with callback on untouch:

    var ui = require("ui")       // load ui base module
         .add(require("uiChk")) // add module into base and remove from cache
          ;
    

    --- Create - specify - check box

    // flgs clazz  id    x  y  w s b f  value object /. callback on 'untouch'
    ui.c( 3,"chk","c1",140,45,25,0,2,7,"H"
                      ,function(id,v){ (v) ? LED2.set() : LED2.reset() }
                      ,[12,7,3,5,"On"]);
    //                  fv t x y  label text
    

    Creates, adds to ui, conditionally displays and returns an active(2),
    visible(1) =(3) checkbox ("chk") with id "c01". Checkbox is positioned
    at 145 @ 40 (left @ top, x @ y) and sized 25 square (both width and
    height), has 2(blue) / 7(white) border / fill colors, is initially
    unchecked (0, but any falsey works), has value object "H" (when checked
    and undefined when unchecked), has callback that sets and resets green
    LED2 accordingly, and is labeled "On" right next to it in fontVector
    (size) 12, text (font) color 7(white) on black screen background color
    and x / y-offset of 3 / 5 from left top corner of checkbox's bounding box.

    Colors are bit-coded with 3-bit color-depth according ui.clrs=[...] setup.

    For ui base, color, label, and callback details see (also) ui base module.

    Callback cb is called on untouch with id, v, ui, e, t as arguments:

    args[] sym   description 
      0    id  : button id ("c1")
      1    v   : value object ("H" - can be any object);
                 NOTE: is undefined when check box is unchecked
      2    ui  : ui (THE ui object)
      3    e   : uiChk element (chk 'object' (runtime data structure))
      4    t   : touch info x, y,...  (where last touched)
            { x : x coordinate
            , y : y coordinate
            , t : touched (truey) / untouched (falsey)
            , f : flags
            }
    

    For detailed ui setup, including color depth and custom colors, connecting
    to display and touch screen, soft key board or buttons, see ui module and
    example material (in the ui.zip file and sources with comments). Take a
    look at the example that shows all ui elements on one single display -

    folder. Make it the Espruino Web IDE sandbox folder and run the ready-made
    examples on your Espruino board. Espruino forum has several entries about
    the modules and hardware. Search in (google) for:
    

    Espruino Forum allObjects touch screen display modular ui framework```

    No board or display with touch screen yet? No Problem: Open uiExample.html
    in uidev folder in Web browser to run the same example in the Cross
    Development / Emulation environment (where modules are developed and
    maintained).

    For helpful details to use the ui base and ui element APIs, take a look
    at documentation in ui base and uiBtn modules.

    --- chk ui element constructor arguments (a[]) and runtime data structure (e[]) are:

    arg runtime 'object' instance of 'clazz' chk (chkbox)
    a[]  e[]
     0   [0] f  - flags focus(4), active(2), visible(1)
     .    .         0bxx1 visible &1 visible 
     .    .         0bx1x active  &2 active / senses touches vs read/display-only
     .    .         0b1xx focus   &4 focus by touch down, drag w/in bounding box
     1   [1] c  - clazz "chk"
     2   [2] i  - id eg "c01", short, at least 2..3 chars,  ui globally unique.
                  Single letter ui element ids are 'reserved' (for keyboard(s)).
     3   [3] x  - x ((left ) of focus / touch bounding box)
     4   [4] y  - y ((top  ) of focus / touch bounding box)
     5       w  - width and height (of focus / touch bounding box)
         [5] x2 - x ((right) of focus / touch bounding box: x - w + 1)
     6       s  - initial checked(truey/1)/unchecked(falsy/0) state used
                  with [9] value info
         [6] y2 - y ((bot  ) of focus / touch bounding box: y - h + 1)
     7   [7] bc - border color
     8   [8] fc - fill color
     9       v  - value (returned value when checked)
         [9] vi - value info array w/ state and value
             [0] - truey/falsey for checked/unchecked state of checkbox
             [1] - value object (returned when checkbox is checked)
    10  [10] cb - simple, preferred callback on untouch after touchdown
    11  [11] l  - label (info), array with:
          l[0]  fv - fontVector (size)
          l[1]  tc - (label) text color
          l[2]  x  - x offset from focus box x ( bounding box left )
          l[3]  y  - y offset from focus box y ( bounding box top  )
          l[4]  tx - label text to display (using .drawString())
    12  [12] ca - NON-preferred, experimental callback on any touch event
    

    Attached shot: Check box c1 and then check box c2 'tapped'.


    1 Attachment

    • uiChkExample.png
  • ----- uiExt ------------------------- 20190927

    The ui extension extends ui base with service and convenience functions for
    ui frame work as well application

    ui extension is currently required by:

    • uiRad - Radio button ui element
    • uiKb3x10B - Soft / touch keyboard / keypad
    • uiEBC - extension for button controlled ui

    --- Brief documentation of the methods / functions available:

    • .ff(a,f,s,_) - find and return first element in array a starting
      with start index s (if absent, s=0 assumed) that makes function
      f(element, _auxiliaryVar) return truey; if not found,
      return undefined. Argument _ (_auxiliaryVar) is also
      passed to the function and can be any value the application needs. If
      application has no use for it, it is just left away in the call and
      function.
    • .fx(a,f,s,_) - find and return index of first element in array a
      that makes function f(element) return truey; if not found,
      return -1. Argument _ (_auxiliaryVar) is also
      passed to the function and can be any value the application needs. If
      application has no use for it, it is just left away in the call and
      function.
    • .e(iie) - return ui element by idx, id or elt. If by id and not found,
      undefined is returned.
    • .g(iie) - get element value (obj) - runtime e[9] - by idx, id or elt.
    • .u(iie,p) - update value object by element idx or id with or without
      propagation (calling callback). Be aware how the value (object) is
      defined creation of the ui element.
    • .t(iie,ud) - tap on ui element by idx or id or ui element. If element is
      found, x and y coordinates of center are passed to ui and processing
      happens as if the element would be touched on a touch screen and then
      un-touched - with the default or overwritten untouch delay .ud.
      Providing proper un-touch delays yields full control over touch-time
      sensitive ui elements, such as key board keys, where, for example,
      long touch of keys with numbers in uiKbc3x10B module will return
      the number character instead of the alpha character.
    • .cb(iie) and .cb(iie,cb,ca) - getter and setter of callback; getter is
      with both cb and ca being undefined by value or absence of value.


    --- ext - ui extension properties - variables and methods - mixed into ui base:

    exports = // ui (generic) ext(ension/utils: vertices(circle,...) find elt/idx in arr,...)
    { mn: "uiExt" // module 'clazz' name - globally unique (used to remove code from cache)
    , ud: 100 // default tap duration defined as u-ntouch d-elay / timeout
    , cg1: [2,-5,4,-4,5,-2,6,0,5,2,4,4,2,5,0,6,-2,5­,-4,4,-5,2,-6,0,-5,-2,-4,-4,-2,-5]
    // ....
    // ...  public methods - documented above
    // ... private methods
    // ...
    }
    

    Attached shot: shows the browser developer / debug view with the browser console log, to which the ui framework is logging to by injected functions.


    1 Attachment

    • uiXDEdebugExample.png
  • ----- uiRad ------------------------- 201909227

    *** ui radio button module

    The ui radio button module extends ui base with radio button ui element.

    Note: It requires the uiExt - ui extension module - to be loaded.

    --- Enable ui base with uiRad element functions:

    Note: requires uiExt(ension) module (see uiExt.js module)

    var ui = require("ui")     // load ui base module
        .add(require("iuExt")) // add uiExt module into base and remove from cache
        .add(require("uiRad")) // add uiRad module into base and remove from cache
        ;
    

    --- Create - specify - radio buttons

    // flgs clazz  id      x  y  w s b f value callback on 'untouch'
    ui.c( 3,"rad","r1.a",  5,80,37,0,6,2,"L",  cb, [12,7,5,10,"Large"]);
    ui.c( 3,"rad","r1.b", 95,85,27,1,3,1,"M",  0 , [12,7,5, 7,"Med"  ]);
    ui.c( 3,"rad","r1.c",170,90,17,1,5,2,"S",  0 , [12,7,5, 2,"Small"]);
    //                                             fv t x  y label text
    

    Crates and add to ui three active(2), visible(1) =(3) radio buttons at
    x/y (left/top: 5/80, 95/85 and 170/90) of different sizes/widths/diameters
    (37, 27 and 17) with size related values ("L", "M" and "S") and labels
    ("Large", "Medium" and "Small"), with group name "r1." (dot is part of
    group name). Medium sized radio button is set/checked (first set/checked
    radio button wins... therefore small one is not set/checked even though
    specified as such). Border colors (6, 3 and 5) and fill colors (2,1 and 2)
    are all different (by choice). Unset/unchecked radiobuttons show ui
    display background color (ui.bc). Label array definitions include
    fontVector (size), font color, x/y offset from right/top (x2/y) corner of
    touch sensitive bounding box, and text.

    Colors are bit-coded with 3-bit color-depth according ui.clrs=[...] setup.

    Callback cb is called on untouch with id, v, ui, e, t as arguments:

    args[] sym   description 
      0    id  : button id ("r1.a")
      1    v   : value object ("L"|"M"|"S" - can be any object)
                 NOTE: is undefined when check box is unchecked
      2    ui  : ui (THE ui object)
      3    e   : uiRad element (rad 'object' (runtime data structure) just untouched)
      4    t   : touch info x, y,... (where last touched)
            { x : x coordinate
            , y : y coordinate
            , t : touched (truey) / untouched (falsey)
            , f : flags
            }
    

    For detailed ui setup, including color depth and custom colors, connecting
    to display and touch screen, soft key board or buttons, see ui module and
    example material (in the ui.zip file and sources with comments). Take a
    look at the example that shows all ui elements on one single display -
    uiExampleAll.js - project file in the _sbx Espruino sandbox projects
    folder. Make it the Espruino Web IDE sandbox folder and run the ready-made
    examples on your Espruino board. Espruino forum has several entries about
    the modules and hardware. Search in (google) for:
    Espruino Forum allObjects touch screen display modular ui framework

    No board or display with touch screen yet? No Problem: Open uiExample.html
    in uidev folder in Web browser to run the same example in the Cross
    Development / Emulation environment (where modules are developed and
    maintained).

    For helpful details to use the ui base and ui element APIs, take a look
    at documentation in ui base and uiBtn modules.

    --- rad ui element constructor arguments (a[]) and runtime data structure (e[]) are:

    arg runtime 'object' instance of 'clazz' chk (chkbox)
    a[]  e[]
     0   [0] f  - flags focus(4), active(2), visible(1)
     .    .         0bxx1 visible &1 visible 
     .    .         0bx1x active  &2 active / senses touches vs read/display-only
     .    .         0b1xx focus   &4 focus by touch down, drag w/in bounding box
     1   [1] c  - clazz - "rad"
     2   [2] i  - id - eg "r01.a","r01.b","r01.c",.. short, at least 2 or 3
                  chars for radio button group id, and globally unique.
                  Single letter ui element ids are 'reserved' (for keyboard(s)).
                  Radio button id consist of two elements:
                    - Radio button group id - the part *before* the dot.
                    - Radio button individual id - the part *after* the dot.
                  Radio button group id is used to make the buttons in the
                  group behave like radio buttons: any 'pressed' button
                  'releases' all others, or in other words, only one radio
                  button can be 'pressed' - or on - at one time.
     3   [3] x  - x ((left ) of focus / touch bounding box)
     4   [4] y  - y ((top  ) of focus / touch bounding box)
     5       w  - width and hight (of focus / touch box,... 
         [5] x2 - x ((right) of focus / touch bounding box: x - w + 1)
     6       s  - initial checked(truey/1)/unchecked(falsey/0) state
                  used w/ [9] value info; note that only one radio
                  button can be set / checked / on at one time
         [6] y2 - y ((bot  ) of focus / touch bounding box: y - h + 1)
     7   [7] bc - border color
     8   [8] fc - fill color
     9       v  - value (returned value when checked)
         [9] vi - value info array w/ state and value object
             [0] - truey / falsey indicating state checked/unchecked
             [1] - value object (returned when radio button is checked)
    10       cb - simple, preferred callback on untouch after touchdown;
        [10] g  - radio button group management array object
             [0] - radio button group id, eg. "r1." (incl. dot)
             [1] - preferred callback (1st encountered truey cb) 
             [2] - alternate callback (1st encountered truey ca)
             [3] - checked radio button ui element (or null / falsey)
             [4...] - group radio button ui elements
                  Note that first encountered truey callback - cb or ca wins
                  and cb wins over ca. Therefore only one needs to be
                  specified per group cb over ca priority. The others can be
                  left undefined, they are ignored anyway.
    11  [11] l  - label (info), array with:
          l[0]  fv - fontVector (size)
          l[1]  tc - (label) text color (index)
          l[2]  x  - x offset from focus box x ( bounding box left )
          l[3]  y  - y offset from focus box y ( bounding box top  )
          l[4]  tx - label text to display (using .drawString())
    12  [12] ca - NON-preferred, experimental callback on any touch event
    

    Attached shot: Tabbed thru uiRad - radio buttons.


    1 Attachment

    • uiRadExample.png
  • ----- uiSli ------------------------- 20190927

    *** ui slider module

    ui slider module extends ui base with slider ui element.

    --- Enable ui base with uiSli and create a slider with callback on untouch:

    var ui = require("ui")       // load ui base module
          .add(require("uiSli")) // add uiSli module into base and remove from cache
          ;
    

    --- Create - specify - a slider

    var s2 = // hold on to "s2" slider ui element for direct access app later on.
    //    0   1    2       3   4   5   6   7   8  9      10  11 
    // flgs  clazz id      x   y   w   h  bc  fs  valObj cb,  ls (arr of label ([[...],..]))
    //	     sli                ->x2->y2                     [0] fv tc   x  y  min max s flgs
    ui.c( 3,"sli","s2"  , 30,175,180, 26, -4,[-4,0],{v:33},cb, [[12, 6, 70, 6,   0,100,3,168
    //                                                          [0][8] format (function)
                                                                ,function(v,_){ // (val, ui)
            var s="", f=_.te.f; return (!(f==0 || f&184)) ? s : ((v<1)?"0":"") // 128+32+16+8
                 +(s=Math.round(v*10)+"%").substr(0,s.len­gth-2)+"."+s.substr(-2); } ] // val
    //                                                     [>0] fv tc    x  y  label text
                                                              ,[12, 7, -25, 6, "%:0"] // frnt
                                                              ,[12, 7, 182, 6, "100"] // back
                                                              ]);
    

    Creates, adds to ui, conditionally displays and returns and stores in
    global variable s2 a 0..100% slider of 3 pixel change value touch
    sensitivity with bit coded callback activation flags, custom colored
    (-4) border and same colored 'left' filling, black (0) 'right'filling,
    shows % range from min 0% to max 100% as text labels left and right of
    of it and current value as label in center on it by custom formatter
    function.

    Custom formatter has dual purpose:

    • render more than just the bare value
    • avoid slowing down sliding by rendering (if needed)

    Because rendering - drawing strings on a display - can be slow, rendering
    the actual value while sliding has to be kept to a minimum to keep sliding
    snappy. Providing the formatter function that returns an non-empty string
    only on desired conditions and an empty one else does the trick: rendering
    an empty string is not noticeably slowing down sliding; ui element value
    (v), ui and touch event states / flags (_, _.te.f) can be used to form
    the desired condition to return empty or not empty string.

    Colors are bit-coded with 3-bit color-depth according ui.clrs=[...] setup.

    Since slider has two (2) areas to fill - left and right side of slider -
    fill colors - fs - are specified as array:

    • fs[0] is fill color for the 'left' / 'filled' side of the slider.
    • fs[1] is fill color for the 'right' / 'empty' side of the slider.

    Since slider has drag aspects callback and should also be able to deliver
    values to the application while sliding, callback conditions can be
    specified by flags as defined for the ui event flags. These flags also
    control whether the new changed value is set and custom formatter function
    is invoked or not. On untouch though, the new value is and formatter is
    invoked no matter whether the untouch flag(s) is(are) set or not.

    Callback is called with id, v, ui, e, t as arguments:

    args[] sym   description 
      0    id  : button id ("c01")
      1    v   : value object (usually a number)
      2    ui  : ui (THE ui object)
      3    e   : uiSli element (sli 'object' (runtime data structure))
      4    t   : touch info x, y,... (where last touched)
            { x : x coordinate
            , y : y coordinate
            , t : touched (truey) / untouched (falsey)
            , f : flags
            }
    

    For detailed ui setup, including color depth and custom colors, connecting
    to display and touch screen, soft key board or buttons, see ui module and
    example material (in the ui.zip file and sources with comments). Take a
    look at the example that shows all ui elements on one single display -
    uiExampleAll.js - project file in the _sbx Espruino sandbox projects
    folder. Make it the Espruino Web IDE sandbox folder and run the ready-made
    examples on your Espruino board. Espruino forum has several entries about
    the modules and hardware. Search in (google) for:
    Espruino Forum allObjects touch screen display modular ui framework

    No board or display with touch screen yet? No Problem: Open uiExample.html
    in uidev folder in Web browser to run the same example in the Cross
    Development / Emulation environment (where modules are developed and
    maintained).

    For helpful details to use the ui base and ui element APIs, take a look
    at documentation in ui base and uiBtn modules.

    --- sli ui element constructor arguments (a[]) and runtime data structure (e[]) are:

    arg runtime 'object' instance of 'clazz' slider
    a[]  e[]
     0   [0] f  - flags focus(4), active(2), visible(1)
     .    .         0bxx1 visible &1 visible 
     .    .         0bx1x active  &2 active / senses touches vs read/display-only
     .    .         0b1xx focus   &4 focus by touch down, drag w/in bounding box
     1   [1] c  - clazz - "rad"
     2   [2] id - eg "s02", short, at least 3 chars, and ui globally unique
                  Single letter ui element ids are 'reserved' (for keyboard(s)).
     3   [3] x  - x ((left ) of focus / touch bounding box)
     4   [4] y  - y ((top  ) of focus / touch bounding box)
     5       w  - width (of focus / touch box,... 
         [5] x2 - x ((right) of focus / touch bounding box: x - w + 1)
     6       h  - height (of focus / touch box,...
         [6] y2 - y ((bot  ) of focus / touch bounding box: y - h + 1)
     7   [7] bc - border color
     8   [8] fs - fill colors array
           fs[0] fill color for 'left side' of slider
           fs[1] fill color for 'right side' of slider
     9   [9] v  - value - a number
    10  [10] cb - callback on touch, value change, untouch
    11  [11] ls - labels - array of labels of which first one is value related
          ls[0] label (info) for value related info, array with:
             l[0]  fv - fontVector (size)
             l[1]  tc - (label) text color (index)
             l[2]  x  - x offset from focus box x ( bounding box left )
             l[3]  y  - y offset from focus box y ( bounding box top  )
             l[4]  mn - minimum value ( at left  border )
             l[5]  mx - maximum value ( at right border )
             l[6]  s  - sensitivity (in pixels to detect change and redraw)
             l[7]  fm - optional format function to format value label
          ls[1,2,3,...] any number of additional labels, but mostly just two...
             l[0]  fv - fontVector (size)                    ...for min and max
             l[1]  tc - (label) text color (index)
             l[2]  x  - x offset from focus box x ( bounding box left )
             l[3]  y  - y offset from focus box y ( bounding box top  )
             l[4]  tx - label text to display (using .drawString())
    

    Attached shot: Slider s1 and slider s2 moved / 'dragged'.


    1 Attachment

    • uiSliExample.png
  • ----- uiInp ------------------------- 20190927

    *** ui input field module

    ui input module extends ui base module with input field ui element.

    Note: requires uiExt(ension) module (see uiExt.js module)

    --- Enable ui with uiInp element functions:

    var ui = require("ui")       // load ui base module
          .add(require("iuExt")) // add module into base and remove from cache
          .add(require("uiInp")) // add module into base and remove from cache
          ;
    

    --- Create and add to ui an active(2), visible(1) =(3) input field at x/y
    (left/top: 45/7) of size width/height (195/28), turkis border color (3),
    white. Callback clears content when tap lasted longer than 550ms and there
    is content. Not checking for content runs into stack overflow, because .u(e,"")
    triggers again a callback because it is a change of value...

    Colors are bit-coded with 3-bit color-depth according ui.clrs=[...] setup.

    //    0  1     2       3   4   5   6   7   8  9       10  11 
    // flgs  clazz id      x   y   w   h  bc  fc  valObj  cb   ls (arr of label ([[..],..]))
    //       btn                ->x2->y2                  callback clears on tap > 550ms
    ui.c( 3,"inp","i1"  , 45,  7,195, 28,  3,  7,"modular UI"
           ,function(i,v,_,e){ if (getTime()-_.tt>0.55   // [0] fv tc    x  y  mxLen typ fmt
                               && v.length) _.u(e,""); } // [1] fv tc    x  y  label text
                                                             ,[[13, 0,   5, 6, 16,   0,  0]
                                                              ,[13, 7,-233, 6, "Field"    ]
                                                              ]);
    

    Callback cb is called on release (untouch) and change and
    provides id, v, ui, e, t as arguments:

    args[] sym   description 
      0    id  : button id ("x")
      1    v   : value (object) - type dependent (for now just string)
      2    ui  : ui (THE ui object)
      3    e   : uiInp element (inp 'object' (runtime data structure))
      4    t   : touch info x, y,...  (where last touched)
            { x : x coordinate
            , y : y coordinate
            , t : touched (truey) / untouched (falsey)
            , f : flags
            }
    

    For detailed ui setup, including color depth and custom colors, connecting
    to display and touch screen, soft key board or buttons, see ui module and
    example material (in the ui.zip file and sources with comments). Take a
    look at the example that shows all ui elements on one single
    display - uiExampleAll.js - project file in the _sbx Espruino sandbox projects
    folder. Make it the Espruino Web IDE sandbox folder and run the ready-made
    examples on your Espruino board. Espruino forum has several entries about
    the modules and hardware. Search in (google) for: Espruino Forum allObjects touch screen display modular ui framework.

    No board or display with touch screen yet? No Problem: Open uiExample.html
    in uidev folder in Web browser to run the same example in the Cross
    Development / Emulation environment (where modules are developed and
    maintained).

    For helpful details to use the ui base and ui element APIs, take a look
    at documentation in ui base and uiBtn modules.

    --- inp ui element constructor arguments (a[]) and runtime data structure (e[]) are:

    arg runtime 'object' instance of 'clazz' inp - input field
    a[]  e[]
     0   [0] f  - flags focus(4), active(2), visible(1)
     .    .         0bxx1 visible &1 visible 
     .    .         0bx1x active  &2 active / senses touches vs read/display-only
     .    .         0b1xx focus   &4 focus by touch down, drag w/in bounding box
     1   [1] c  - clazz "inp"
     2   [2] i  - id eg "i01", short, at least 2..3 chars,  ui globally unique.
                  Single letter ui element ids are 'reserved' (for keyboard(s)).
     3   [3] x  - x ((left ) of focus / touch bounding box)
     4   [4] y  - y ((top  ) of focus / touch bounding box)
     5       w  - width (of focus / touch box,... 
         [5] x2 - x ((right) of focus / touch bounding box: x - w + 1)
     6       h  - height (of focus / touch box,...
         [6] y2 - y ((bot  ) of focus / touch bounding box: y - h + 1)
     7   [7] bc - border color
     8   [8] fc - fill color
     9   [9] v  - value - (string) value to start with
    10  [10] cb - simple, preferred callback on untouch after touchdown
    11  [11] ls - labels - array of labels of which 1st is also value related   
          ls[0] label (info) for value related info, array with:
             l[0]  fv - fontVector (size)
             l[1]  tc - (label) text color (index)
             l[2]  x     - x offset from focus box x ( bounding box left )
             l[3]  y     - y offset from focus box y ( bounding box top  )
             l[4]  mxLen - maximum length
             l[5]  type  - opt type (absent & 0:plain string, only type for now)
             l[6]  frmt  - opt format function to use to format value for label
          ls[1,2,3,...] any number of additional labels, but mostly just two...
             l[0]  fv - fontVector (size)                     ...for min and max
             l[1]  tc - (label) text color (index)
             l[2]  x  - x offset from focus box x ( bounding box left )
             l[3]  y  - y offset from focus box y ( bounding box top  )
             l[4]  tx - label text to display (using .drawString())
    

    Attached shot: 'cleared' entry field tapping x in top right corner; 'typed' on keyboard by tapping: 1 x shift (one shot shift sh1 ^^) - Key K - 3 x shift or long tap (num lock #^) - key 3 - 1 x shift (back to no shift sh0 ^) - y key, 2 x shift (shift lock sh2 ^^^) - Keys B O A R D - 2 x shift (back to no shift sh0 ^) - key x - long tap bottom right button - back space / bs - removes last keyed x.


    1 Attachment

    • uiInpAndkeyBoardExample.png
  • ----- keyboard/keypad - 3 x 10 Btns ------------------------- 20190927

    *** uiKbd3x10B - ui keyboard module w/ 3 rows x 10 columns of uiBtn ui elements.

    Note: uiKbd3x10B IS NOT part of the ui singleton. It is a standalone
    input ui element similar to the touch modules: connected to ui and ui
    elements, but not mixed into the ui element and managed by the ui (yet
    or ever).

    --- Key assugnments and mappings

    For Keyboards with buttons, key ids are the button ids, and button ids are
    SINGLE character ids (thus reserved for the ui modules and not available
    to application ui elements). The id of a key is the 1st char on the key
    (btn), for examples:

     - "a" for key (btn) "a@"  , returning the values "a"    , "A", and "@".
     - "<" for key (btn) "<' \", returning the values "cr/lf", "'", and "\".
     - "." for key (btn) "
    

    Keyboard key handler calls cb callback w/ values v, k, u, e, and t parameters:

      v - key value - single character representing the printable key; special
          values: "sh0".."sh3" for shift key, "bs" for BacksSpace, "cr/lf" for
          CR/LF 
      k - keyboard object (this)
      u - ui core singleton
      e - event source (key btn)
      t - touch object
    

    e is the key board button that has just been untouched (k: keyboard, u: ui
    core and t: touch event object).

    Key value depends on short TAP / LONG TOUCH and shift key mode.

    The ^-shift key cycles w/ short taps through ^ m=0, ^^ m=1, ^^^ m=2,
    ^# m=3 its displays and modes; long touch always goes directly to
    ^# m=3 shift mode (number and special characters - NUM-LOCK).

    For REGULAR keys, shift key modes and returned chars with regular - SHORT
    TAP - are:

    shift   shift
    key     mode
    display    impact (and for mode 1 only also state change)
      ^     0: lowercase   - key id/1st char in lowercase
      ^^    1: 1 uppercase - key id/1st char in uppercase, falls back to mode 0.
      ^^^   2: all uppercase, key id/1st char in uppercase (SHIFT LOCK)
      ^#    3: all number and special chars - 2nd char on key (NUM LOCK)
    

    LONG TOUCH return number for key with numbers - top row - and for other
    keys with three (3) characters this third character (except cr/lf key),
    otherwise none (cr/lf key is double special: special 'special key').

    The ^-shift key itself returns key values: "sh"+mode: "sh0", "sh1", "sh2"
    and "sh3".

    Special keys with shift mode 0..3 returns (listed by key, mode and short
    tap or long touch):

    special key w/ [chars on key] - location on keyboard
    1st
    chr
    on  shift shft
    key displ mod returns  (comment)
    --- ----- --- -------- ----------------------------------------­-----------
    ^ (shift)[^],[^^],[^^^],[#^] - 1st key in bottom row
      ... w/ short tap:
        ^     0:  sh1     (when sh0 / ^ no-shift,     cycles to sh1, single shot)
        ^^    1:  sh2     (when sh1 / ^^ single shot  cycles to sh2)
        ^^^   2:  sh3     (when sh2 / ^^^ shift lock, cycles to sh3)
        #^    3:  sh0     (when sh3 / #^ NumLock,     cycles back to sh0)
      ... w/ long touch:
        N/C   *:  sh3     (ALWAYS jumps to mode 3 / "sh3" string for NumLock)
    
    < (=cr/lf) [<'\] - last key in 2nd to bottom row:
      ... w/ short tap:
        ^     0:  cr/lf   ("cr/lf" string)
        ^^    1:  '       (single quote) and back to shift mode 0
        ^^^   2:  \       (back slash)
        #^    3:  cr/lf   ("cr/lf" string)
      ... w/ long touch:
    
    . (dot) [.?..] - second to last key in bottom row:
      ... w/ short tap:
        ^     0:  .       (dot)
        ^^    1:  ?       (question mark) and back to shift mode 0
        ^^^   2:  .       (dot) 
        #^    3:  .       (dot)
      ... w/ long touch:
        ^     0:  .       (dot)
        ^^    1:  ?       (question mark) and back to shift mode 0
        ^^^   2:  .       (dot)
        #^    3:  .       (dot)
    
    b/blank [ "_] - last key in bottom row:
      ... w/ short tap:
        ^     0:  b/blank (space 'bar')
        ^^    1:  "       (double quotes) and back to shift mode 0
        ^^^   2:  _       (underscore)
        #^    3:  b/blank (space 'bar')
      ... w/ long touch:
        N/A   *:  bs      (ALWAYS "bs" string for BackSpace)
    

    In other words - basically:

    • When no-shift (^): first char on key prints.
    • Tap shift once (^^) prints next char in uppercase or 2nd special key on special key.
    • Tap shift twice goes into SHIFT-LOCK (^^^) and twice again goes back to no-shift.
    • When in SHIFT-LOCK (^^^): char key prints uppercase and special key 3rd char.
    • Touch shift long goes always into NUM-LOCK.
    • When in NUM-LOCK (#^): key with number prints number, special key.
    • Touch long key with number or 3rd, special char prints number and special char, resp.
    • Tapping shift cycles from no-shift to single-shot shift, SHIFT-LOCK,
      NUM-LOCK and back to no-shift.


    --- Usage example

    get the keyboard and 'connect' it to entry field "i1" - as part of UI definition:

    Note: this keyboard modules requires ui base and uiBtn modules to be
    loaded. It creates for each key a button with the id being the first char
    displayed on the key, for example, a,b,c,..., ^ for shift, < for enter /
    return, blank (" ") for blank/backspace,... Therefore, (practically) all
    single letter ui element id's are taken and therefore 'reserved'.

    var ui = require("ui")     // load ui base module
        .add(require("uiBtn")) // add module into base and remove from cache
        ;
    var kbd = require("uiKbd3x10B")
                      .connect(ui,0,215,24,24, 7, 7,-2, 1, 1,10 ,0, 2, 4,
    //                         ui,x,  y, w, h,bc,fc,sc,mx,my,fs,tc,tx,ty,cb
      function(v) {
        var s = ui.g("i1"); // is uiInp(ut field) ui element 
        if (v.length === 1) {  'normal' key - append
          ui.s("i1",s + "" + v);
        } else if (v === "bs") { BS/BackSpace key - remove last char
          if (s.length > 0) { ui.s("i1",s.substr(0,s.length - 1)); } 
        }
      } );
    

    --- Module .connect(arguments)

     0  ui  ui core
     1  x   top of bounding box border
     2  y   left of bounding box border
     3  w   width of bounding box
     4  h   height of bounding box
     5  bc  key border color
     6  fc  key fill color for regular keys
     7  sc  key fill color for special keys (shift, return, special chars,...
     8  mx  x margin of key bounding box            ...only, blank, backspace)
     9  my  y margin of key bounding box
    10  fv  key fontVector (size)
    11  tc  key text color 
    12  tx  key text x offset (within key/btn bounding box)
    13  ty  key text y offset (within key/btn bounding box)
    14  cb  callback function to accept following parameters:
       0  v   key: a single printable char, string for non-printables:
              "bs" for backspace, "cr/lf", "sh0".."sh4" for shift key
       1  k   keyboard (this sigleton)
       1  u   ui core singleton (see ui module)
       2  e   key btn that was just released (see uiBtn module)
       3  t   touch object (see ui module)
    

    --- uiKbd3x10B - soft keyboard properties - variables and methods - mixed into ui base:

    exports =  // uiKbd3x10B
    { mn: "uiKbd3x10B"
    , kc: 30   // key count
    , fi: -1   // idx 1st key in ui.es ui elts; adjust when insert/remove before
    , fk: null // first key ("q")
    , lt: 0.14 // short touch maximum / medium ('long') touch min time in seconds
    , tt: 0    // touch time in secs
    , sm: 0    // shift mode (0="^", 1="^^", 2="^#", 3="^^^")
    , sk: null // shift key
    , cb: null // callback
    , st: ["^","^^","^^^","#^"] // for shift key status display
    // ....
    // ...  public methods - documented above
    // ... private methods
    // ...
    }
    
  • ----- uiEBC ------------------------- 20190927

    The ui Extension for Button Controlled ui vs touch screen controlled ui.

    ui EBC extension requires uiExt module.

    The uiEBC extension works already well with plain buttons, check boxes and
    radio buttons with just watched buttons. Even though it works also with
    the uiKbd3x10B soft keyboard and uiInp input field module when choosing
    and passing the proper un-touch delay(s). Better support is under
    construction.

    The uiExampleAll shows in emulation how to use 1 to three buttons to
    navigate and 'tap' / 'touch' a ui with buttons, check boxes, and radio
    buttons.

    The extension supports select next ui element, select previous ui element
    and 'tap' selected ui element. By default, select next and previous cycle
    thru all active and visible buttons (flags: 0b0011 = 3) with wrap around.
    Optional first and last index values can be supplied to stay within a
    a range of ui elements, such as a keyboard or keypad when in an input
    field. Wrap around can be controlled was well.

    The uiEBC extension is added to the base ui the same way as the uiExt
    extension module. uiEBC extension requires iuExt extension to be loaded
    in order to work.

    var ui = require("ui")
        ,add(require("uiBtn"))
        .add(require("uiExt"))
        ,add(require("uiEBC"))
        ;
    

    There can be only one ui element selected at one time. The visual cue is
    left and top borders of a rectangle in touch color.

    --- Brief documentation of the methods / functions available:

    • .sd - delay for re-drawing the selection cue after ui element has been
      tapped / (un)touched - which concludes with a blur. Application
      can control / overwrite select redraw delay as well as the (un)touch
      delay by passing values to tap/touch selected ui element mimicking
      tap / touch duration : .st(sd,ud). Passing a value for du will
      override the default value specified in ui base module.
    • .sx - property holds index of selected element in ui element array .es.
      Value -1 indicates that no ui element is selected.
    • .sn(w,f,l) - method selects the next qualifying ui element. If there is
      none (anymore, if not wrapped), undefined is returned. If there is
      only exactly one, then that one is returned every time. If none is
      currently selected, the first one is returned.
    • .st(sd,ud) - method taps / touches the selected ui element and delays the
      untouch by specified time overriding the .ud default as defined
      in the uiExt module, where the tap / touch function .t(iie,ud)
      is defined.

    Select next and previous will be constrained in the future by a bit map
    in addition to the range to proved more flexibility over non-contiguous
    ranges of qualifying and application desired ui elements.

    --- EBC - ui extension for push button controlled ui - properties - variables
    and methods - mixed into ui base:

    exports = // ui ctrl w/ btns vs touch ext, used by ui3|4|5|6BC modules
    { mn: "uiEBC" // module 'clazz' name - globally unique (used to rem frm cache)
    , sd: 100 // ms default select delay
    , sx: -1 // idx elt selected w/ ui ctrl w/ phys push btns, no touch
    // ....
    // ...  public methods - documented above
    // ... private methods
    // ...
    }
    

    Attached shots: 3, 2 and 1 physical push button example added. With small change in keyboard and input field, the ui can be driven by as little as 1 button. Of course, more buttons increase efficiency. The visual cue that a ui element is selected are the bright horizontal and vertical lines above and left of the ui element.


    2 Attachments

    • uiButtonControlledUI2.png
    • uiButtonControlledUI1.png
  • ----- ui on Pixl.js ------------------------- 20190927 - *** PREVIEW ***

    First of all: Espruino Pixl.js is just cool... - I guess you know that... so:

    I created and example for Pixl... worked right out of box... with proper color converter as soon as I understood Pixl's color philosophy and designing the ui in black&white. Vector fonts do not work out that great in the desired (small) size.... and with that some changes arose. The solution approach for fonts is the same ways as custom colors: fonts are added into an array and referenced with 'negative font vector (sizes): -1 as font means: pick the font that has been added as the first one. The changes made so far have not been back-ported and tested yet for non-Pixl displays, but they will... and they will enhance actually the overall framework. Because Pixl is a bit shorter on memory than PICO and I wanted still to work on the un-minified sources, I produced _U - uncommented versions.... and so far, these include the changes needed for Pixl.

    So that you can start to play with the ui framework on Pixle, I added the sandbox _sbx that holds all what you need for this example - foremost the _U versions.

    Below code of the uiExamplePixl.js project file that produces the UI shown in attached screenshot. I chose three (3) buttons to control the ui: BTN1 (left top) and BTN2 (right top) jump between ui elements - previous and next - und BTN3 (right bottom) is the enter / touch button. In attached screenshot, you see the first - unchecked -checkbox ui element selected. Pressing BTN3 will check the checkbox and fire the callback.

    // uiExamplePixl.js - pixl - example
    
    // 20190930 - allObjects - variables: ~ / ~
    
    // ----- setup lon and log() (considering emulating _eConsole)
    var lon = true // logging is on / off
      , log = function() { if (lon) { var lgr;(lgr=(typeof _eConsole
           =="object")?_eConsole:console).log.apply­(lgr,arguments);}};
    
    // ----- pull in ui base and ui element(s) - (..._U = uncommented)
    var ui = require("ui_U")                 // basic module _U(ncommented)
        .adx(require("uiExt_U"),-1,"uiExt_U") // basic extension
        .adx(require("uiEBC_U"),-1,"uiEBC_U") // button controlled ui extension
        .adx(require("uiBtn_U"),-1,"uiBtn_U") // ui button element
        .adx(require("uiChk_U"),-1,"uiChk_U") // ui checkbox element
        .adx(require("Font6x8"),-2,"Font6x8") // pixl suitable bitmap font
        ;
    // define Pixl Button 'clazz'
    var uiPBt = function(btn,downCb,upCb,debounce) {
      var t=0;
      setWatch( function(e){ if (! t) { t=Math.round(e.time*1000); if (downCb) downCb(t); }}
              , btn, {edge:"rising" ,repeat:true, debounce:debounce} );
      setWatch( function(e){ if (  t) { upCb(Math.round(e.time*1000)-t); t=0; }}
              , btn, {edge:"falling",repeat:true,debounce:deb­ounce} );
    };
    
    
    // ------- uiPBt -----------------------------------
    
    // ----- define callback(s): preferred (untouch) / generic (all) touch event(s)
    var cb = function(id, v, _, e, t) { log( "id:",id,"val:",v
            ,(t)?((t.t)?"":"un")+"touch focused":t); }
      , ca = ( (typeof ca === "undefined")
             ? function(id, v, _, e, t) { var s=(t.f|65536).toString(2); // log flags
                 log("c:",e[1],"c:",e[1],"id:",id,"v:",v,­"f:",[s.substr(5,4),s.substr(9,4),s.subs­tr(13,4)].join(" "),t.f); }
             : ca )
      , u; // for space / memory / var saving value of * undefined *
    
    // ----- define UI (in level 0 - built to be saved)
    
    //---------- Button examples ----------
    //
    //    0  1     2       3   4   5   6   7   8  9       10  11                         12
    // flgs  clazz id      x   y   w   h  bc  fc  valObj  cb,  l (label array obj)
    //       btn                ->x2->y2                        fv tc    x  y label txt  ca
    ui.c( 3,"btn","b1"  ,  0, 20, 30, 19,  0,  0, "B_1",  cb,  [-1, 7,   7, 6, "RED" ]);
    ui.c( 3,"btn","b2"  , 29, 20, 41, 19,  0,  7, {v:1},  ca , [-1, 0,   7, 6, "Y-w-B"], ca);
    
    // ---------- Checkbox examples ----------
    //    0  1      2      3   4   5   6   7   8  9       10  11                         12
    // flgs  clazz id      x   y   w   s  bc  fc  valObj  cb,  l (label array obj)
    //       chk                ->x2+>y2~------->[s,vOjb]       fv tc    x  y label txt
    ui.c( 3,"chk","c1"  , 69, 20, 19,  0,  0,  7, "H",    cb,  [-1, 0,   1, 6, "On"   ]);
    ui.c( 3,"chk","c2"  , 99, 20, 19,  1,  0,  0, "X",    u ,  [-1, 0,   1, 6, "Up"   ], ca);
    
    //----- Pixl sepcifics
    var dsp = g, dspW=128, dspH=64 // ...to keep var names as usual
      ;
    ui.bc=7; // set ui background color to white
    ui.tc=0; // set ui touch color to black
    ui.clrs= // set white to pixl color 
        [function(c,i){ var v=(i)?c^7:c; return [(v&&4)?0:1]; }.bind(ui)
        ];
    var flp;
    
    // ----- run UI
    function onInit() {
      dsp.clear(); LED.set(); // display clear and turn back light on
      setTimeout(function(){
        // connect ui to dsp and display it
        ui.connect(dsp)
          .w(0,0,dspW-1,dspH-1) // wipe screen (rect) w/ default / bg color
          .d()                  // display all elements
          .di = true            // set display changes to immediate
          ;
        flp = function(){ dsp.flip(); };
        flp();
        new uiPBt(BTN1,0,function(t){console.log(1);­ui.sp();flp();}); // sel prev on BTN1
        new uiPBt(BTN2,0,function(t){console.log(2);­ui.sn();flp();}); // sel next on BTN2
        new uiPBt(BTN3,0,function(t){console.log(3);­ui.st(t+70,t);setTimeout(flp,t+140);});
      },1000);
    }
    setTimeout(onInit,999); // for dev only, remove before save()
    

    Remaining ui elements will be adjusted to work also on Pixl and changes will be back ported, examples updated, documentation adjusted... and emulation will follow thereafter. This all will take 'a few' days. In the mean time:

    • happy Pixl-ing @ allObjects - w/ attached sandbox.

    Attached: _sbx - Sandbox folder with all what Pixl needs... and a screenshot (related to chosen font size... can go smaller or larger... all a simple matter of the UI definitions...).


    2 Attachments

  • ui on other displays - tbp

  • ui experimental all touch events callback - drag and drop - tbp

  • ----- varia ----------- DisplayUiEltWithCumstomFormatter.js

    *** Varia: Diaplay ui element with custom formatter

    Custom renderers / formatters may take ui and touch states / flags into
    account in their logic. This can cause interference issues when using
    ui's display function on element: ui.d(e). Issue is that sometimes
    the renderer / formatter works, and sometimes not.

    There are three (3) options for a solution:

    --- Setup

    The ui singleton (framework)

    var ui = require("ui")  // UI singleton holding slider s2 (below)
    // ...
    

    slider "s2" kept held onto with variable s2:

    var s2 = ui.c( 3,"sli","s2",... // slider - uiSli - w/ custom formatter taking ui / touch event
    	 // states / flags into consideration in logic
         // function(v,_) { if (_t.te.f... ) { ...
    

    For more details see instructions in ui, uiBtn and uiSli modules.

    --- Options

    • option 1

      // option 1 - where no touch event is ongoing and flags f has to keep state:
      //   set touch event flags all to 0 before invoking display of ui element
      
      ui.te.f = 0; ui.d(s2); // display value label of slider
      
    • option 2

      // option 2 - where touch event is ongoing but flags f have to keep state:
      //   backup flags, set all to 0 or as desired, invoke display AND restore
      var fs = ui.te.f; ui.te.f=0; ui.d(s2).te.f=fs;
      
    • option 3

      // option 3 - use additional control flag
      //   introduce a flag yourself 'outside' / global or on ui which works as
      //   a one-shot trigger in the render / formatter function - or as in
      //   the example code below: stick it as property to the ```ui``` object
      //  (just use property name 6 or more chars).
      function(v,_) {
      if (_.myOverrideFlag || !_.te.t) {
      _.myOverrideFlag=0;
      // ...the other stuff
      } }
      //
      ui.myOverrideFlag=1; ui.d(s2);
      
    • option 4

      // option 4 - extend ui with .dWrapped(e) wrapping of .d(e) and
      // using option 2 for implementation
      ui.dWrapped(e) = function (){var fs=this.te.f; this.d(e).te.f=fs; return this;};
      // ...
      ui.dWrapped(e);
      
  • ui varia 2 - tbp

  • ui varia 3 - tbp

  • ----- 2019-09-24 Update -----

    Finally some rework and review round trip completed to share things that had its design in 2015, some rework in 2016 and 2017 and just now.

    Updated zip file is to be found in --->post #7 .

    Reviewing and getting code and some doc ready for pub did not go without some enhancements and adding a few vars to the foot print.

    Event categorization with flags and handling got a significant lift to give more control to the user and application. Also the slider got a technical as well as a face lift. Some issues got resolved, such as ui elements changing unintended their values by just dragging over them.

    Input field is still a bit bare bones and kept as such... last but not least because of slowness of (communication to) most displays. Have to come up w/ some enhanced version... being a different ui element 'clazz' though to keep the simple version with small foot print.

    The general idea of publishing in this conversation is to get it out and get feedback. Over time these posts will change including the zip file uploaded in post #8 with all the code - modules, examples and cross development components (on functional changes). Old versions will be kept there as long as possible.

    After a while things will move into regular site's modules folder and into module and tutorial pages.

    Cu after reading up from ---> post #7 on.

  • ----- 2019-09-27 Update -----

    As new extension module has been added: support for control with watched push buttons rather than with touch screen - see ---> post #17 . Same time, the keyboard, the input fields and the uiExt extension got some enhancements, last but not least to support watched buttons to drive the ui. So far all but the sliders can be used - with three (3) buttons it is very comfortable: two (2) buttons to cycle forward and backward thru he ui elements and the third - 'Enter' / Tap - button is for tapping. Timing the tapping - how long the push button has been pressed and passing it when calling the tap selected ui element - .ts(...) - gives access to also the timed functions:

    • short tap / touch / press the button: < 140 ms
    • medium tap / touch / press the button: 140..450 ms
    • long tap / touch / press the button: >450 ms

    The input field has a callback that checks tap time, and when it is more than 550 ms, the input field is cleared. The delete x in the top right corner of the input field is part of the ui element and not one of its own, so it cannot be reached with cycling through the ui elements. Having separate buttons for navigation / 'moving' the selection and the action tapping makes operation safe. Increasing the time for clearing the input field to make it distinctive from cycling backwards with a long press.

    Other, non-functional changes is the renaming of the example: replacing the DspTouch infix in the names with just Example provides room the examples to also include button operation vs touch operation. Both can be used simultaneously... (with one caveat: touchscreen tapping does not preserve the visual cue what ui element is selected at times.

    Updated zip file is to be found in --->post #7 .

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Modular and extensible UI framework and ui elements.

Posted by Avatar for allObjects @allObjects

Actions