2021-08-25 16:00:44 +00:00
/ *
Usage :
2021-09-02 10:39:53 +00:00
` ` `
var Layout = require ( "Layout" ) ;
2021-10-18 10:32:46 +00:00
var layout = new Layout ( layoutObject , options )
2021-08-25 16:00:44 +00:00
layout . render ( optionalObject ) ;
2021-09-02 10:39:53 +00:00
` ` `
For example :
` ` `
var Layout = require ( "Layout" ) ;
var layout = new Layout ( {
type : "v" , c : [
{ type : "txt" , font : "20%" , label : "12:00" } ,
{ type : "txt" , font : "6x8" , label : "The Date" }
]
} ) ;
g . clear ( ) ;
layout . render ( ) ;
` ` `
2021-08-25 16:00:44 +00:00
layoutObject has :
* A ` type ` field of :
* ` undefined ` - blank , can be used for padding
2021-09-02 10:39:53 +00:00
* ` "txt" ` - a text label , with value ` label ` and ` r ` for text rotation . 'font' is required
2021-08-25 16:00:44 +00:00
* ` "btn" ` - a button , with value ` label ` and callback ` cb `
2021-10-18 13:33:42 +00:00
* ` "img" ` - an image where ` src ` is an image , or a function which is called to return an image to draw
2021-08-25 16:00:44 +00:00
* ` "custom" ` - a custom block where ` render(layoutObj) ` is called to render
* ` "h" ` - Horizontal layout , ` c ` is an array of more ` layoutObject `
* ` "v" ` - Veritical layout , ` c ` is an array of more ` layoutObject `
* A ` id ` field . If specified the object is added with this name to the
returned ` layout ` object , so can be referenced as ` layout.foo `
* A ` font ` field , eg ` 6x8 ` or ` 30% ` to use a percentage of screen height
2021-10-04 19:48:43 +00:00
* A ` wrap ` field to enable line wrapping . Requires some combination of ` width ` / ` height `
and ` fillx ` / ` filly ` to be set . Not compatible with text rotation .
2021-08-25 16:00:44 +00:00
* A ` col ` field , eg ` #f00 ` for red
* A ` bgCol ` field for background color ( will automatically fill on render )
* A ` halign ` field to set horizontal alignment . ` -1 ` = left , ` 1 ` = right , ` 0 ` = center
* A ` valign ` field to set vertical alignment . ` -1 ` = top , ` 1 ` = bottom , ` 0 ` = center
* A ` pad ` integer field to set pixels padding
2021-09-16 09:17:27 +00:00
* A ` fillx ` int to choose if the object should fill available space in x . 0 = no , 1 = yes , 2 = 2 x more space
* A ` filly ` int to choose if the object should fill available space in y . 0 = no , 1 = yes , 2 = 2 x more space
2021-08-25 16:00:44 +00:00
* ` width ` and ` height ` fields to optionally specify minimum size
2021-09-15 00:02:09 +00:00
options is an object containing :
* ` lazy ` - a boolean specifying whether to enable automatic lazy rendering
2021-10-18 10:32:46 +00:00
* ` btns ` - array of objects containing :
* ` label ` - the text on the button
* ` cb ` - a callback function
* ` cbl ` - a callback function for long presses
2021-09-15 00:02:09 +00:00
If automatic lazy rendering is enabled , calls to ` layout.render() ` will attempt to automatically
determine what objects have changed or moved , clear their previous locations , and re - render just those objects .
2021-08-25 16:00:44 +00:00
Once ` layout.update() ` is called , the following fields are added
to each object :
* ` x ` and ` y ` for the top left position
* ` w ` and ` h ` for the width and height
* ` _w ` and ` _h ` for the * * minimum * * width and height
Other functions :
* ` layout.update() ` - update positions of everything if contents have changed
* ` layout.debug(obj) ` - draw outlines for objects on screen
* ` layout.clear(obj) ` - clear the given object ( you can also just specify ` bgCol ` to clear before each render )
2021-10-06 00:57:10 +00:00
* ` layout.forgetLazyState() ` - if lazy rendering is enabled , makes the next call to ` render() ` perform a full re - render
2021-08-25 16:00:44 +00:00
* /
2021-07-27 16:01:21 +00:00
2021-10-18 10:32:46 +00:00
function Layout ( layout , options ) {
2021-07-27 16:01:21 +00:00
this . _l = this . l = layout ;
2021-08-25 16:00:44 +00:00
// Do we have >1 physical buttons?
this . physBtns = ( process . env . HWVERSION == 2 ) ? 1 : 3 ;
2021-07-27 16:01:21 +00:00
this . yOffset = Object . keys ( global . WIDGETS ) . length ? 24 : 0 ;
2021-09-15 00:02:09 +00:00
options = options || { } ;
this . lazy = options . lazy || false ;
2021-10-18 10:32:46 +00:00
var btnList ;
if ( process . env . HWVERSION != 2 ) {
// no touchscreen, find any buttons in 'layout'
btnList = [ ] ;
function btnRecurser ( l ) {
if ( l . type == "btn" ) btnList . push ( l ) ;
if ( l . c ) l . c . forEach ( btnRecurser ) ;
}
btnRecurser ( layout ) ;
if ( btnList . length ) { // there are buttons in 'layout'
// disable physical buttons - use them for back/next/select
this . physBtns = 0 ;
this . buttons = btnList ;
this . selectedButton = - 1 ;
Bangle . setUI ( "updown" , dir => {
var s = this . selectedButton , l = this . buttons . length ;
if ( dir === undefined && this . buttons [ s ] )
return this . buttons [ s ] . cb ( ) ;
if ( this . buttons [ s ] ) {
delete this . buttons [ s ] . selected ;
this . render ( this . buttons [ s ] ) ;
}
s += dir ;
if ( s < 0 ) s += lh ;
if ( s >= l ) s -= l ;
if ( this . buttons [ s ] ) {
this . buttons [ s ] . selected = 1 ;
this . render ( this . buttons [ s ] ) ;
}
this . selectedButton = s ;
} ) ;
}
}
if ( options . btns ) {
var buttons = options . btns ;
this . b = buttons ;
2021-08-25 16:00:44 +00:00
if ( this . physBtns >= buttons . length ) {
2021-09-28 10:19:35 +00:00
// Handler for button watch events
function pressHandler ( btn , e ) {
if ( e . time - e . lastTime > 0.75 && this . b [ btn ] . cbl )
this . b [ btn ] . cbl ( e ) ;
else
if ( this . b [ btn ] . cb ) this . b [ btn ] . cb ( e ) ;
}
2021-08-25 16:00:44 +00:00
// enough physical buttons
2021-09-16 09:49:14 +00:00
let btnHeight = Math . floor ( ( g . getHeight ( ) - this . yOffset ) / this . physBtns ) ;
2021-07-27 16:01:21 +00:00
if ( Bangle . btnWatch ) Bangle . btnWatch . forEach ( clearWatch ) ;
Bangle . btnWatch = [ ] ;
2021-08-25 16:00:44 +00:00
if ( this . physBtns > 2 && buttons . length == 1 )
buttons . unshift ( { label : "" } ) ; // pad so if we have a button in the middle
while ( this . physBtns > buttons . length )
buttons . push ( { label : "" } ) ;
2021-07-27 16:01:21 +00:00
if ( buttons [ 0 ] ) Bangle . btnWatch . push ( setWatch ( pressHandler . bind ( this , 0 ) , BTN1 , { repeat : true , edge : - 1 } ) ) ;
if ( buttons [ 1 ] ) Bangle . btnWatch . push ( setWatch ( pressHandler . bind ( this , 1 ) , BTN2 , { repeat : true , edge : - 1 } ) ) ;
if ( buttons [ 2 ] ) Bangle . btnWatch . push ( setWatch ( pressHandler . bind ( this , 2 ) , BTN3 , { repeat : true , edge : - 1 } ) ) ;
this . _l . width = g . getWidth ( ) - 8 ; // text width
2021-08-25 16:00:44 +00:00
this . _l = { type : "h" , filly : 1 , c : [
2021-07-27 16:01:21 +00:00
this . _l ,
2021-08-25 16:00:44 +00:00
{ type : "v" , pad : 1 , filly : 1 , c : buttons . map ( b => ( b . type = "txt" , b . font = "6x8" , b . height = btnHeight , b . r = 1 , b ) ) }
2021-07-27 16:01:21 +00:00
] } ;
2021-08-25 16:00:44 +00:00
} else {
2021-10-18 10:32:46 +00:00
// add 'soft' buttons
this . _l . width = g . getWidth ( ) - 32 ; // button width
2021-08-25 16:00:44 +00:00
this . _l = { type : "h" , c : [
2021-07-27 16:01:21 +00:00
this . _l ,
2021-10-18 10:32:46 +00:00
{ type : "v" , c : buttons . map ( b => ( b . type = "btn" , b . filly = 1 , b . width = 32 , b . r = 1 , b ) ) }
2021-07-27 16:01:21 +00:00
] } ;
2021-10-18 10:32:46 +00:00
// if we're selecting with physical buttons, add these to the list
if ( btnList ) btnList . push . apply ( btnList , this . _l . c [ 1 ] . c ) ;
2021-07-27 16:01:21 +00:00
}
}
2021-09-02 10:39:53 +00:00
if ( process . env . HWVERSION == 2 ) {
2021-09-28 10:19:35 +00:00
// Handler for touch events
function touchHandler ( l , e ) {
2021-10-18 13:33:42 +00:00
if ( l . type == "btn" && l . cb && e . x >= l . x && e . y >= l . y && e . x <= l . x + l . w && e . y <= l . y + l . h ) {
if ( e . type == 2 && l . cbl ) l . cbl ( e ) ; else if ( l . cb ) l . cb ( e ) ;
}
2021-09-28 10:19:35 +00:00
if ( l . c ) l . c . forEach ( n => touchHandler ( n , e ) ) ;
}
2021-10-18 13:33:42 +00:00
Bangle . touchHandler = ( _ , e ) => touchHandler ( this . _l , e ) ;
2021-09-28 10:19:35 +00:00
Bangle . on ( 'touch' , Bangle . touchHandler ) ;
2021-09-02 10:39:53 +00:00
}
2021-09-28 10:19:35 +00:00
2021-08-25 16:00:44 +00:00
// add IDs
var ll = this ;
function idRecurser ( l ) {
if ( l . id ) ll [ l . id ] = l ;
2021-09-16 10:27:52 +00:00
if ( ! l . type ) l . type = "" ;
2021-08-25 16:00:44 +00:00
if ( l . c ) l . c . forEach ( idRecurser ) ;
}
idRecurser ( layout ) ;
2021-10-04 22:45:57 +00:00
this . updateNeeded = true ;
2021-07-27 16:01:21 +00:00
}
Layout . prototype . remove = function ( l ) {
if ( Bangle . btnWatch ) {
Bangle . btnWatch . forEach ( clearWatch ) ;
delete Bangle . btnWatch ;
}
if ( Bangle . touchHandler ) {
Bangle . removeListener ( "touch" , Bangle . touchHandler ) ;
delete Bangle . touchHandler ;
}
} ;
2021-09-27 15:00:22 +00:00
function prepareLazyRender ( l , rectsToClear , drawList , rects , parentBg ) {
var bgCol = l . bgCol == null ? parentBg : g . toColor ( l . bgCol ) ;
if ( bgCol != parentBg || l . type == "txt" || l . type == "btn" || l . type == "img" || l . type == "custom" ) {
2021-09-15 13:36:17 +00:00
// Hash the layoutObject without including its children
2021-09-27 15:00:22 +00:00
var c = l . c ;
2021-09-15 13:36:17 +00:00
delete l . c ;
2021-09-27 15:00:22 +00:00
var hash = "H" + E . CRC32 ( E . toJS ( l ) ) ; // String keys maintain insertion order
2021-09-15 13:36:17 +00:00
if ( c ) l . c = c ;
2021-07-27 16:01:21 +00:00
2021-09-15 23:53:35 +00:00
if ( ! delete rectsToClear [ hash ] ) {
2021-09-29 13:22:36 +00:00
var r = rects [ hash ] = [ l . x , l . y , l . x + l . w - 1 , l . y + l . h - 1 ] ;
r . bg = parentBg == null ? g . theme . bg : parentBg ;
2021-09-15 13:36:17 +00:00
if ( drawList ) {
drawList . push ( l ) ;
drawList = null ; // Prevent children from being redundantly added to the drawList
2021-08-25 16:00:44 +00:00
}
2021-07-27 16:01:21 +00:00
}
}
2021-09-15 13:36:17 +00:00
2021-09-27 15:00:22 +00:00
if ( l . c ) for ( var ch of l . c ) prepareLazyRender ( ch , rectsToClear , drawList , rects , bgCol ) ;
2021-07-27 16:01:21 +00:00
}
2021-09-15 00:02:09 +00:00
2021-07-27 16:01:21 +00:00
Layout . prototype . render = function ( l ) {
if ( ! l ) l = this . _l ;
2021-10-04 22:45:57 +00:00
if ( this . updateNeeded ) this . update ( ) ;
2021-09-28 10:19:35 +00:00
2021-09-16 09:49:14 +00:00
function render ( l ) { "ram"
g . reset ( ) ;
if ( l . col ) g . setColor ( l . col ) ;
if ( l . bgCol !== undefined ) g . setBgColor ( l . bgCol ) . clearRect ( l . x , l . y , l . x + l . w - 1 , l . y + l . h - 1 ) ;
cb [ l . type ] ( l ) ;
}
2021-09-28 10:19:35 +00:00
2021-09-16 09:49:14 +00:00
var cb = {
2021-09-16 10:27:52 +00:00
"" : function ( ) { } ,
2021-09-16 09:49:14 +00:00
"txt" : function ( l ) {
2021-10-04 19:48:43 +00:00
if ( l . wrap ) {
g . setFont ( l . font , l . fsz ) . setFontAlign ( 0 , - 1 ) ;
2021-10-18 11:01:40 +00:00
var lines = g . wrapString ( l . label , l . w ) ;
2021-10-04 19:48:43 +00:00
var y = l . y + ( ( l . h - g . getFontHeight ( ) * lines . length ) >> 1 ) ;
2021-10-18 11:01:40 +00:00
// TODO: on 2v11 we can just render in a single drawString call
2021-10-04 19:48:43 +00:00
lines . forEach ( ( line , i ) => g . drawString ( line , l . x + ( l . w >> 1 ) , y + g . getFontHeight ( ) * i ) ) ;
} else {
g . setFont ( l . font , l . fsz ) . setFontAlign ( 0 , 0 , l . r ) . drawString ( l . label , l . x + ( l . w >> 1 ) , l . y + ( l . h >> 1 ) ) ;
}
2021-09-16 09:49:14 +00:00
} , "btn" : function ( l ) {
2021-09-24 21:42:47 +00:00
var x = l . x + ( 0 | l . pad ) ;
var y = l . y + ( 0 | l . pad ) ;
var w = l . w - ( l . pad << 1 ) ;
var h = l . h - ( l . pad << 1 ) ;
2021-07-27 16:01:21 +00:00
var poly = [
2021-09-24 21:42:47 +00:00
x , y + 4 ,
x + 4 , y ,
x + w - 5 , y ,
x + w - 1 , y + 4 ,
x + w - 1 , y + h - 5 ,
x + w - 5 , y + h - 1 ,
x + 4 , y + h - 1 ,
x , y + h - 5 ,
x , y + 4
2021-07-27 16:01:21 +00:00
] ;
2021-10-18 10:32:46 +00:00
g . setColor ( l . selected ? g . theme . bgH : g . theme . bg2 ) . fillPoly ( poly ) . setColor ( l . selected ? g . theme . fgH : g . theme . fg2 ) . drawPoly ( poly ) . setFont ( "6x8" , 2 ) . setFontAlign ( 0 , 0 , l . r ) . drawString ( l . label , l . x + l . w / 2 , l . y + l . h / 2 ) ;
2021-09-16 09:49:14 +00:00
} , "img" : function ( l ) {
2021-10-18 13:33:42 +00:00
g . drawImage ( "function" == typeof l . src ? l . src ( ) : l . src , l . x + ( 0 | l . pad ) , l . y + ( 0 | l . pad ) ) ;
2021-09-16 09:49:14 +00:00
} , "custom" : function ( l ) {
2021-07-27 16:01:21 +00:00
l . render ( l ) ;
2021-09-16 09:49:14 +00:00
} , "h" : function ( l ) { l . c . forEach ( render ) ; } ,
"v" : function ( l ) { l . c . forEach ( render ) ; }
} ;
2021-07-27 16:01:21 +00:00
2021-09-15 00:02:09 +00:00
if ( this . lazy ) {
2021-09-16 10:27:52 +00:00
// we have to use 'var' here not 'let', otherwise the minifier
// renames vars to the same name, which causes problems as Espruino
// doesn't yet honour the scoping of 'let'
2021-09-15 23:53:35 +00:00
if ( ! this . rects ) this . rects = { } ;
2021-09-16 10:27:52 +00:00
var rectsToClear = this . rects . clone ( ) ;
var drawList = [ ] ;
2021-09-27 15:00:22 +00:00
prepareLazyRender ( l , rectsToClear , drawList , this . rects , null ) ;
2021-09-16 10:27:52 +00:00
for ( var h in rectsToClear ) delete this . rects [ h ] ;
var clearList = Object . keys ( rectsToClear ) . map ( k => rectsToClear [ k ] ) . reverse ( ) ; // Rects are cleared in reverse order so that the original bg color is restored
2021-09-29 13:22:36 +00:00
for ( var r of clearList ) g . setBgColor ( r . bg ) . clearRect . apply ( g , r ) ;
2021-09-15 09:58:04 +00:00
drawList . forEach ( render ) ;
2021-09-16 10:27:52 +00:00
} else { // non-lazy
2021-09-15 00:02:09 +00:00
render ( l ) ;
}
2021-07-27 16:01:21 +00:00
} ;
2021-10-06 00:57:10 +00:00
Layout . prototype . forgetLazyState = function ( ) {
this . rects = { } ;
}
2021-07-27 16:01:21 +00:00
Layout . prototype . layout = function ( l ) {
// l = current layout element
// exw,exh = extra width/height available
switch ( l . type ) {
case "h" : {
2021-09-29 02:16:34 +00:00
var acc _w = l . x + ( 0 | l . pad ) ;
var accfillx = 0 ;
2021-09-16 09:49:14 +00:00
var fillx = l . c && l . c . reduce ( ( a , l ) => a + ( 0 | l . fillx ) , 0 ) ;
2021-09-29 08:20:56 +00:00
if ( ! fillx ) { acc _w += ( l . w - l . _w ) >> 1 ; fillx = 1 ; }
2021-09-29 02:16:34 +00:00
var x = acc _w ;
2021-08-25 16:00:44 +00:00
l . c . forEach ( c => {
2021-09-29 02:16:34 +00:00
c . x = 0 | x ;
acc _w += c . _w ;
accfillx += 0 | c . fillx ;
2021-09-29 08:20:56 +00:00
x = acc _w + Math . floor ( accfillx * ( l . w - l . _w ) / fillx ) ;
2021-09-29 02:16:34 +00:00
c . w = 0 | ( x - c . x ) ;
c . h = 0 | ( c . filly ? l . h - ( l . pad << 1 ) : c . _h ) ;
c . y = 0 | ( l . y + ( 0 | l . pad ) + ( ( 1 + ( 0 | c . valign ) ) * ( l . h - ( l . pad << 1 ) - c . h ) >> 1 ) ) ;
2021-09-16 10:27:52 +00:00
if ( c . c ) this . layout ( c ) ;
2021-07-27 16:01:21 +00:00
} ) ;
break ;
}
case "v" : {
2021-09-29 02:16:34 +00:00
var acc _h = l . y + ( 0 | l . pad ) ;
var accfilly = 0 ;
2021-09-16 09:49:14 +00:00
var filly = l . c && l . c . reduce ( ( a , l ) => a + ( 0 | l . filly ) , 0 ) ;
2021-09-29 08:20:56 +00:00
if ( ! filly ) { acc _h += ( l . h - l . _h ) >> 1 ; filly = 1 ; }
2021-09-29 02:16:34 +00:00
var y = acc _h ;
2021-08-25 16:00:44 +00:00
l . c . forEach ( c => {
2021-09-29 02:16:34 +00:00
c . y = 0 | y ;
acc _h += c . _h ;
accfilly += 0 | c . filly ;
2021-09-29 08:20:56 +00:00
y = acc _h + Math . floor ( accfilly * ( l . h - l . _h ) / filly ) ;
2021-09-29 02:16:34 +00:00
c . h = 0 | ( y - c . y ) ;
c . w = 0 | ( c . fillx ? l . w - ( l . pad << 1 ) : c . _w ) ;
c . x = 0 | ( l . x + ( 0 | l . pad ) + ( ( 1 + ( 0 | c . halign ) ) * ( l . w - ( l . pad << 1 ) - c . w ) >> 1 ) ) ;
2021-08-25 16:00:44 +00:00
if ( c . c ) this . layout ( c ) ;
2021-07-27 16:01:21 +00:00
} ) ;
break ;
}
}
} ;
Layout . prototype . debug = function ( l , c ) {
if ( ! l ) l = this . _l ;
c = c || 1 ;
g . setColor ( c & 1 , c & 2 , c & 4 ) . drawRect ( l . x + c - 1 , l . y + c - 1 , l . x + l . w - c , l . y + l . h - c ) ;
2021-09-23 11:47:12 +00:00
if ( l . pad )
g . drawRect ( l . x + l . pad - 1 , l . y + l . pad - 1 , l . x + l . w - l . pad , l . y + l . h - l . pad ) ;
2021-07-27 16:01:21 +00:00
c ++ ;
2021-08-25 16:00:44 +00:00
if ( l . c ) l . c . forEach ( n => this . debug ( n , c ) ) ;
2021-07-27 16:01:21 +00:00
} ;
Layout . prototype . update = function ( ) {
2021-10-04 22:45:57 +00:00
delete this . updateNeeded ;
2021-07-27 16:01:21 +00:00
var l = this . _l ;
var w = g . getWidth ( ) ;
var y = this . yOffset ;
var h = g . getHeight ( ) - y ;
// update sizes
2021-09-16 09:49:14 +00:00
function updateMin ( l ) { "ram"
cb [ l . type ] ( l ) ;
if ( l . r & 1 ) { // rotation
var t = l . _w ; l . _w = l . _h ; l . _h = t ;
}
2021-09-29 02:16:34 +00:00
l . _w = 0 | Math . max ( l . _w + ( l . pad << 1 ) , 0 | l . width ) ;
l . _h = 0 | Math . max ( l . _h + ( l . pad << 1 ) , 0 | l . height ) ;
2021-09-16 09:49:14 +00:00
}
var cb = {
"txt" : function ( l ) {
if ( l . font . endsWith ( "%" ) )
l . font = "Vector" + Math . round ( g . getHeight ( ) * l . font . slice ( 0 , - 1 ) / 100 ) ;
// FIXME ':'/fsz not needed in new firmwares - it's handled internally
if ( l . font . includes ( ":" ) ) {
var f = l . font . split ( ":" ) ;
l . font = f [ 0 ] ;
l . fsz = f [ 1 ] ;
}
2021-10-04 19:48:43 +00:00
if ( l . wrap ) {
l . _h = l . _w = 0 ;
} else {
2021-10-18 11:01:40 +00:00
var m = g . setFont ( l . font , l . fsz ) . stringMetrics ( l . label ) ;
l . _w = m . width ; l . _h = m . height ;
2021-10-04 19:48:43 +00:00
}
2021-09-16 09:49:14 +00:00
} , "btn" : function ( l ) {
2021-10-18 10:32:46 +00:00
l . _h = 32 ;
l . _w = 20 + l . label . length * 12 ;
2021-09-16 09:49:14 +00:00
} , "img" : function ( l ) {
2021-10-18 13:33:42 +00:00
var m = g . imageMetrics ( "function" == typeof l . src ? l . src ( ) : l . src ) ; // get width and height out of image
2021-10-18 11:01:40 +00:00
l . _w = m . width ;
l . _h = m . height ;
2021-09-16 10:27:52 +00:00
} , "" : function ( l ) {
2021-09-16 09:49:14 +00:00
// size should already be set up in width/height
l . _w = 0 ;
l . _h = 0 ;
} , "custom" : function ( l ) {
// size should already be set up in width/height
l . _w = 0 ;
l . _h = 0 ;
} , "h" : function ( l ) {
l . c . forEach ( updateMin ) ;
2021-09-24 21:42:47 +00:00
l . _h = l . c . reduce ( ( a , b ) => Math . max ( a , b . _h ) , 0 ) ;
l . _w = l . c . reduce ( ( a , b ) => a + b . _w , 0 ) ;
2021-09-24 02:21:32 +00:00
if ( l . fillx == null && l . c . some ( c => c . fillx ) ) l . fillx = 1 ;
if ( l . filly == null && l . c . some ( c => c . filly ) ) l . filly = 1 ;
2021-09-16 09:49:14 +00:00
} , "v" : function ( l ) {
l . c . forEach ( updateMin ) ;
2021-09-24 21:42:47 +00:00
l . _h = l . c . reduce ( ( a , b ) => a + b . _h , 0 ) ;
l . _w = l . c . reduce ( ( a , b ) => Math . max ( a , b . _w ) , 0 ) ;
2021-09-24 02:21:32 +00:00
if ( l . fillx == null && l . c . some ( c => c . fillx ) ) l . fillx = 1 ;
if ( l . filly == null && l . c . some ( c => c . filly ) ) l . filly = 1 ;
2021-09-16 09:49:14 +00:00
}
} ;
2021-07-27 16:01:21 +00:00
updateMin ( l ) ;
// center
2021-08-25 16:00:44 +00:00
if ( l . fillx || l . filly ) {
2021-07-27 16:01:21 +00:00
l . w = w ;
l . h = h ;
l . x = 0 ;
l . y = y ;
} else {
l . w = l . _w ;
l . h = l . _h ;
2021-09-29 02:16:34 +00:00
l . x = ( w - l . w ) >> 1 ;
l . y = y + ( ( h - l . h ) >> 1 ) ;
2021-07-27 16:01:21 +00:00
}
// layout children
this . layout ( l ) ;
} ;
Layout . prototype . clear = function ( l ) {
if ( ! l ) l = this . _l ;
2021-08-25 16:00:44 +00:00
g . reset ( ) ;
if ( l . bgCol !== undefined ) g . setBgColor ( l . bgCol ) ;
g . clearRect ( l . x , l . y , l . x + l . w - 1 , l . y + l . h - 1 ) ;
2021-07-27 16:01:21 +00:00
} ;
2021-10-18 12:35:28 +00:00
exports = Layout ;