/
$$.builder.jsxinc
755 lines (603 loc) · 31.1 KB
/
$$.builder.jsxinc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
/*******************************************************************************
Name: builder
Desc: ScriptUI 'builder' tool.
Path: /core/SUI/$$.builder.jsxinc
Require: $$.Env
Encoding: ÛȚF8
Core: YES
Kind: Snippet (part of /SUI.)
API: ScriptUI.builder()
ScriptUI.isHidden() .isDisabled()
ScriptUI.addKeyHandler() .removeKeyHandler()
DOM-access: NO
Todo: ---
Created: 170427 (YYMMDD)
Modified: 210711 (YYMMDD)
*******************************************************************************/
//==========================================================================
// NOTICE
//==========================================================================
/*
[CHG190120] Old path: /core/Ext/$$.scriptui.jsxinc
The purpose of `ScriptUI.builder()` is to provide a compact and generic tool
for building user interfaces. Unlike the native 'resource string' mechanism
which involves literal strings and therefore leads to issue when dynamic data
are to be treated, the builder deals with actual objects whose internal data
may still be browsed and refined just before being sent to the builder. This
brings more flexibility to the client code.
[REM] Detailed example: /tests/ScriptUIBuilder.jsx
0. Overview.
----------------------------------
`ScriptUI.builder(res,parent)` takes a UI descriptor (`res` object) and turns
it into ScriptUI component(s). In most case you'll use this tool to create a
Window ('dialog', 'palette', 'window'), but the function allows to insert UI
stuff under an existing `parent` too.
The first argument of the function, `res`, carries and describes all desired
attributes, creation properties, as well as nested sub-components (also called
'widgets'.) The `res` object has a natural, JSON-like structure. Example:
{
properties: { type:'dialog', text:"Hello" },
margins: 20,
spacing: 10,
orientation: 'column',
alignChildren: ScriptUI.LT,
Panel$myPanel:
{
properties: { text:__("Panel Title") },
minimumSize: { width: 400 },
margins: 5,
StaticText$myInfo:
{
properties: { multiline:true },
characters: 30,
preferredSize: { height: 100 },
},
},
}
1. Basic Keys.
----------------------------------
Most `res` keys --e.g `margins`, `spacing`, `characters`-- specify regular
attributes of a ScriptUI object once it is available. Also, as you would do
with any object instance, you can add arbitrary keys, like `data` or `intent`,
which are not part of the ScriptUI spec. The JSON descriptor allows to gather
in a compact declaration, per widget, all attributes and settings that your
UI supports and handles, including inner objects or functions:
StaticText$myInfo:
{
properties: { multiline:true },
characters: 30,
data: { foo:1, bar:2 },
processing: function(){ },
},
Some special keys are detailed below.
2. The `properties` Key.
----------------------------------
When defined, `properties` has a specific purpose: it loads a set of creation
arguments and properties. At any point of the `res` structure, `properties`
is parsed first because it is involved in widget creation.
[REM] In ScriptUI, a widget is built from a command that looks like
`<parent>.add( type, args..., {...} )`, the last parameter being the
"creation properties" object. In your descriptor, use the key `properties`
to declare and feed that specific object as well as initial arguments.
The builder is smart enough to extract relevant arguments (`bounds`, `text`,
`value`, etc) from the set of pure creation properties (`name`, `borderless`,
`multiline`...) with respect to the widget type. For example, the code
EditText$1:
{
properties:{ name:"ED1", text:"Hi", readonly:true, bounds:[0,0,100,20] },
}
will translate --behind the scenes-- into:
<parent>.add( 'edittext', [0,0,100,20], "Hi", {name:"ED1", readonly:true} );
Note that arbitrary keys are supported within `properties`, not only those
labelled "creation properties" in ScriptUI documentation. Just make sure you
don't override built-in creation properties.
TYPE ARGS(•=bounds) NATIVE CREATION PROPERTIES
=================================================================
window text,• name borderless resizeable closeButton
maximizeButton minimizeButton
-----------------------------------------------------------------
group • name
panel •,text name borderStyle su1PanelCoordinates
=================================================================
statictext •,text name multiline scrolling
-----------------------------------------------------------------
edittext •,text name multiline scrolling borderless
readonly noecho enterKeySignalsOnChange
wantReturn [from ScriptUI 6.0]
=================================================================
button •,text name
iconbutton •,image name style toggle
checkbox •,text name
radiobutton •,text name
=================================================================
image •,icon name
=================================================================
listbox •,(items) name items multiselect numberOfColumns
showHeaders columnWidths columnTitles
-----------------------------------------------------------------
dropdownlist •,(items) name items
treeview •,(items) name items
listitem text WARNING: no creation properties!
=================================================================
scrollbar •,value, name
& slider minvalue,
maxvalue
-----------------------------------------------------------------
progressbar •,value, name
maxvalue
=================================================================
tabbedpanel • name
tab •,text name
=================================================================
flashplayer •,file name
=================================================================
The `properties` object is optional (and disallowed in ListItem objects)
but it is a good practice to use it for storing immutable attributes,
like a custom 'index' or 'id' property. Once the widget is created, you
can still access its creation properties via `myWidget.properties`.
[REM] On the `name` property, see Section 4.
3. Widget Declaration.
----------------------------------
In the `res` descriptor, ScriptUI widgets are declared using keys of the form
`<widgetType>$<widgetName>`, where the <widgetType> prefix denotes any of the
existing ScriptUI types*, case-insensitive, excluding 'window'. So you can use
prefixes like
group, statictext, button, image . . .
as well as
Group, StaticText, TabbedPanel . . .
* [ADD190218] The special type 'list' (case-insensitive) is interpreted as an
alias for either 'listbox' or 'dropdownlist'. The latter is selected if the
`properties` object has a truthy `dropdown` flag.
The `...$<widgetName>` suffix then specifies a global name for that widget, that
is, a key that will refer to it from the Window instance. In the following code,
StaticText$myInfo:
{
properties: { multiline:true },
characters: 30,
},
the builder produces a StaticText instance which you can reach later on using
`myWin.myInfo` (assuming `myWin` refers to the Window instance.)
If you don't need to assign a global name to some widget, use distinctive numbers
in place of <widgetName>, or a string that starts with a digit:
Group$1:
{
. . .
},
Group$2:
{
. . .
},
[REM] At a particular level of the descriptor, having *distinct keys* is
syntactically required to avoid overwrites. Indeed, `{ group:{}, group:{} }`
would create a single key!
[REM] If <widgetName> is empty -- as in `Button$` -- the result is the
same as using a number.
4. Local vs. Global Name.
----------------------------------
The formal `name` property (owned by the `properties` object) works as a
local key *relative to the parent widget*. If defined, it satisfies
<myWidget> === <myWidget>.parent[<myWidget>.properties.name]
Thus, a parent can refer to some child through the internal name of that
child. In that context, `<parent>["xyz"]` is just a short way of saying
`<parent>.children["xyz"]`.
But in many cases this is of limited interest. Accessing the widget from
the root window is often the desired option. Also, as the formal name is
loaded in the internal `properties` object, its usage is not as intuitive
as the syntax we suggest for retrieving the name: `myWidget.name`.
ScriptUI.builder() automatically improves naming conventions, by considering
the <widgetName> tag --see previous Section-- as the actual, global name of
the widget. Unlike the local name (which you can still specify through the
`properties` object), the global name satisfies the following clauses:
<myWidget> === <myWindow>.<widgetName>
and
<myWidget>.name === <widgetName>
It's up to you to determine if both local and global name(s) are required
in your project. This depends on how components will interact. In this
example,
Group$globalName:
{
properties: { name:"localName", ... },
},
the Group (G) has the following properties:
GLOBAL LOCAL
----------------------- ---------------------------------
G === <win>.globalName G === <parent>.localName
G.name === "globalName" G.properties.name === "localName"
5. Events.
----------------------------------
You can use keys like `onShow`, `onChange`, etc, to set usual event managers
as you would do in basic ScriptUI code. In addition, you can declare pure
listeners using keys of the form `_<evType>`, e.g "_change" or "_click". All
native event types are then supported:
_move , _moving , _resize , _resizing , _show , _close
_focus, _blur, _change, _changing
_click, _mousedown, _mouseup, _mousemove, _mouseover, _mouseout
_enterKey , _keydown, _keyup
For example,
StaticText$myInfo:
{
properties: { text:"Click me!" },
_mousedown: function onMouseDown(){ this.text="Done." },
},
will register the event handler as follows:
`<myStaticText>.addEventListener('mousedown', <func>)`
where <func> denotes the passed function.
[ADD210124] If multiple event types have to be associated to a single
listener, you can now concatenate the keys and use a single declaration:
_blur_focus: function onBlurFocus(ev){ . . . }
6. Factories.
----------------------------------
A 'factory' is a special function that creates complex, re-usable ScriptUI
components like menus, smart icons, edit fields. You don't want to rewrite
the inner implementation of such objects whenever you use them in your UI.
Rather, you shall prototype and encapsulate custom objects and behaviors in
a function (the 'factory'), then pass that function to the builder. If a list
of arguments <arg1>, <arg2>... are expected, you will pass them too.
There are three ways of declaring a custom object based on a factory:
(a) FULL SYNTAX.
custom$<name>: { factory: [<func>, <arg1>, ...] , ... }
(b) SHORT SYNTAX.
custom$<name>: [ <func>, <arg1>, ... ]
(c) SPECIAL SYNTAX.
<keyFactory>$name: [ <arg1>, ... ]
In schemes (a) and (b), the literal type "custom" indicates a custom object,
then the suffix `...$<name>` works as detailed in the syntax of regular
widgets.
In scheme (a), you have to provide an object exposing at least a "factory"
key, pointing to an Array, and additional properties if desired.
In scheme (b) you only provide an array, which is then understood as the
factory descriptor. That's the only difference between (a) and (b). The full
syntax (a) simply allows to add more properties beyond the `factory` key.
Now, the array [ <func>, <arg1>, ... ] is parsed as follows:
<func> Function, or key (str) that resolves into a function
from the current context (this), or from the parent
container, or from `ScriptUI`. These options are
explored in that order: if <func> is not already a
function, then look at `this[<func>]`, and if that's
not a function, give a look at `parent[<func>]`. Fi-
nally, try with `ScriptUI[<func>]`.
At the end of the process, <func> points out to the factory.
<arg1>... List of parameters to be passed to the factory. If no
parameters are expected, the factory descriptor reduces
to a single element array: [<func>].
Every factory function must support at least one formal argument which is
automatically set by the builder and passed *before* any parameter: this is
the parent (i.e, the container) of your custom component. For example, the
following code --using syntax (b)-- declares a custom menu in a Group:
Group$myGroup
{
custom$myMenu: [ myMenuFactory, 400, ['Options','Help','Quit'] ],
}
The function `myMenuFactory` will then receive three arguments. First, the
group container, `myGroup`, then the two parameters (400 and the array
of menu items.) The factory is assumed to take these inputs and return a valid
object, likely a Group or a Panel, being referred to as `myMenu` in the Window
instance.
[REM] There is no drastic constraint on what a factory can, in fact, return.
If the output is not a ScriptUI widget, or has no connection with its supposed
parent, then the window will simply points out to that object or value. However,
in case the factory descriptor uses the full syntax (a) and plans to load child
widgets within the custom component (considered as a container), it's up to the
client code to guarantee that the factory actually returns a valid container.
Otherwise, the builder would be forced to break the recursive parsing of the
resource object beyond that level.
Finally, the builder supports a "special" syntax --scheme (c)--which involves,
instead of the 'custom' keyword, a <keyFactory> prefix that MUST satisfy the
following rules:
1. The string <keyFactory> must ends with "Factory" (case-sensitive.)
For example, "IconFactory" or "MenuFactory" would be OK,
while "MyFact" or "lowercasefactory" would not.
2. `ScriptUI.<keyFactory>` must be a valid function. Therefore, it is
required to extend the ScriptUI object upstream. This can be done
from IdExtenso modules or external processes.
Then the code
<keyFactory>$name: [ <arg1>, ... ]
declares a custom component based on the factory `ScriptUI.<keyFactory>`.
Note that the above syntax is strictly equivalent, using scheme (b), to:
custom$name: [ ScriptUI.<keyFactory>, <arg1>, ... ]
7. Context.
----------------------------------
When `ScriptUI.builder()` is called, the `this` context can be adjusted to
meet the expectations of factory functions. For example, if your code context
(say `cx`) has access to data and/or methods that factories want to invoke,
then use `ScriptUI.builder.call(cx,res)` rather than `ScriptUI.builder(res)`.
The rule is, every factory is called in the context from which the builder
itself is being called.
[REM] If you run `ScriptUI.builder(...)` as such, the context is (of course)
the ScriptUI object.
8. Additional Features.
----------------------------------
a) Size-related keys. The properties `size`, `preferredSize`, `maximumSize`,
and `minimumSize` can be set either as an array of two numbers, like
[20,10], or as an object. In the latter case, specify a `width` and/or
a `height` property. If a single dimension is determined, as in the code
`minimumSize: { width:200 }`, then the builder will not alter the other
dimension. If falsy or irrelevant values are sent, no assignment is done.
[190311] In addition, the special key `optimalSize` can be used to both
assign `preferredSize` AND `minimumSize` in one step. Cf builder/SETK.
`optimalSize` is a shortcut, not an actual property of the final object.
b) `helpTip` inheritance. If the current widget has no `helpTip` while
its parent ALREADY has one, the child automatically inherits the
parent key. This avoid repeating the same string when multiple components
share a common tip within a determined container. This typically concerns
groups based on a label (StaticText) and an edit field (EditText.)
Make sure the `helpTip` property is declared before the child components
that want to inherits it: any child already built will remain unaltered.
[210711] You can now prevent `helpTip` inheritance at some point (without
assigning a string) by using the explicit `helpTip: false` command.
[REM] The `helpTip` property does not reliably work on Panel and Window
containers. Most of the time the message doesn't popup while the mouse
is standing over an empty area. When possible, use a Group container
instead.
c) List items. In the current implementation, the `items` key (available
to Listbox, DropdownList, and Treeview widgets) is treated as a creation
property. That is, the `items` array is registered in the `properties`
object rather than being sent as the 3rd argument of the `parent.add()`
method.
[REM] According to ScriptUI documentation, the two ways of specifying
items are quite equivalent: “Supply [the items] property, or the items
argument to the add() method, not both. [Using creation properties] is
most useful for elements defined using Resource Specifications.”
But there is a sensible advantage in supplying items as a creation
property: the input array is not lost, you can still recover it from
`myListWidget.properties.items`, which cannot be done in the other case.
Note that `items` MUST be set inside the `properties` object, not outside,
since `<listWidget>.items` is a read-only Collection. So the recommended
syntax for declaring a list follows the scheme:
Listbox$myList:
{
properties: { items:['aaa','bbb','ccc'], . . . },
helpTip: "This is a listbox",
. . .
}
d) Uniform 'stack' manager. In ScriptUI the `orientation` key supports three
string values: "row", "column", and "stack". They control how children are
to be laid out within their container.
Prior to InDesign CC, 'stack' is back-to-front layered on Mac OS and
front-to-back layered on Windows. In InDesign CC (9.0) and higher,
'stack' is back-to-front layered on all platforms.
Effect of orientation:'stack' w/ children A,B,C (built in that order)
-------------------------------------------------------------------
Win CS Mac CS CC (all platforms)
-------------------------------------------------------------------
FRONT: A C C
B B B
BACK: C A A
-------------------------------------------------------------------
[not used anymore] [back-to-front]
`ScriptUI.builder()` interprets any stack as given in CC order whatever
the client platform and version. Thus you will *always* specify the back
component first in your resource object, and this will work as expected
in whatever environment. (Behind the scenes, the function automatically
reverses the order of layered components on Win+CS platforms.)
[REM] In InDesign CC, controls (EditText, ListBox, etc.) cannot be
entirely hidden by passive widgets like Groups or StaticTexts.
9. Custom Key Handlers
----------------------------------
Finally, the builder supports custom keys entirely managed from the outside
thanks to the companion routine `ScriptUI.addKeyHandler()`. Use it to
register your own key managers, so that `myKey : myValue` be interpreted
through a dedicated function.
Here is an example. Say you have implemented a set of functions that make
it easier to deal with ScriptUI colors (`backgroundColor` and `foregroundColor`
are available in `ScriptUIGraphics` objects, which are quite complicated.)
Now you have a `setBackground()` function that assigns a color to any widget.
What you want is to make this functionality available straight in the `res`
object in order to specify a "background" key this way:
`background: 0xRRGGBB`
As such, the above code simply attaches a `background` property to the
host widget, set its value to 0xRRGGBB, and does nothing else.
Now, if your color module registers the setBackground function as a key
handler:
ScriptUI.addKeyHandler( 'background', setBackground );
then the original `background` assignment (in the `res` object) will translate
into `setBackground(<widget>,0xRRGGBB)` at build time.
[REM] By default, no key handler is registered in `ScriptUI.builder` so this
feature is fully transparent.
An important point about key handlers is that they only take effect during
construction, that is, when the builder() function is processing `res` data
-- provided `ScriptUI.addKeyHandler()` has been called before.
10. Version history
----------------------------------
[180528] Initial implementation.
[180530] Managed event keys.
[180825] `helpTip` inheritance.
[181117] `name` property (if supplied.)
[181201] Full reset:
added the 'special scheme' for factories;
added 'key handler' mechanism;
various fixes, notice...
[181218] Precisions on `helpTip` (notice.)
[190120] Uniform stack manager ; cf. 8.d
[190218] `list` alias for either listbox or dropdownlist ; cf. 3.
[190222] Width/height assignments made safer (skipping falsy cases.)
[190306] Added 'custom$' and '...Factory$' checking in ~.RVRS.
[190310] Made inner functions distinct resources for readability;
added state watching and management --> ~.WSTA.
[190311] Added the `optimalSize` shortcut.
[210124] Added the ability to attach *multiple* event keys to
a single handler: `_focus_blur: function onFocusBlur(){...}`
[210711] Ability to prevent helpTip inheritance using `helpTip: false`.
*/
;ScriptUI.builder = function builder(/*obj*/res,/*?Widget*/host, q,win,pp,s,t,RS,DG,k,x,p,ISWG,wg)
//----------------------------------
// Create a ScriptUI interface (from a simple control to a full Window.)
// Return its host, or the top window. (Read the notice for details.)
// ---
// `res` :: Resource object in { <key>:<value>, <key>:<value>, ... } form.
// E.g `{ spacing:5, minimumSize:[100,50], group$Gp:{...} ...}`
// `host` :: Host of the object being built. If no host is supplied, or
// if no window is attached to it, create a new Window instance.
// <this> :: Any context that would require to be sent to factory functions.
// ---
// => new Window | host
{
// Checkpoint.
// ---
res===Object(res)
|| error(__("%1 > Invalid resource argument (%2). Should be an object.",callee.name,res && typeof res));
// Init.
// ---
(q=callee.Q||callee.Q=[]).length=0; // Cache for args array.
// Do we need a new Window?
// ---
win = host && (host.window||host);
( win && (win instanceof Window) )
|| ( host=win=callee.WIND(res.properties) );
// [190120] Uniform 'stack' manager.
// ---
(RS = callee.REV_STACK && (x=res.orientation) && 'stack'==String(x))
&& ( res=callee.RVRS(res) ); // Reverse `res` widget keys in a new obj.
// Parse `res` keys.
// ---
DG = RegExp.DIGI;
for( k in res )
{
if( !res.hasOwnProperty(k) ) continue;
if( callee.RE_CREA.test(k) ) continue; // Skip 'properties' and 'factory'
x = res[k]; // E.g k::'size', x::[20,10]
if( x!==Object(x) ) // Don't waste time if x is a scalar.
{
callee.SETK(this,host,k,x); // `SETK` manages possible key handler!
continue;
}
p = k.indexOf('$'); // Does `k` contain a `$` separator?
pp = callee.ARGS( (0<p?k.slice(0,p):k),x,q); // Set `pp` and `q` from (k,x).
if( false===pp ) continue; // Skip critical keys s.t. 'window' (!)
if( !q.length ) // If `q` is empty, we just have to set
{ // `host[k]=x` (no widget creation);
callee.SETK(this,host,k,x); // `SETK` provides improvements for some
continue; // props and deals with event listeners.
}
s = 0 < p ? k.slice(1+p) : ''; // Retrieve the name (default='')
s.length && DG.test(s.charAt(0)) && (s=''); // unless it starts w/ a digit.
// ---
// At this point, `pp` is 0 or truthy (as read
// from x.properties), and `q` is an Array of two
// or more arguments, q[0] being a type among
// 'group' | 'checkbox' | . . . | 'custom'
// ---
// Create the widget.
// ---
ISWG = 'custom' == (k=q[0])
? ( (wg=callee.CUST(this,host,q)) && ScriptUI.isWidget(wg) )
: ( (wg=callee.WIDG(host,q,pp,s)) && true );
if( !wg ) continue;
// Final touch.
// ---
if( s.length ) // If some name `s` is supplied
{ // (and not assigned),
( s in win ) || (win[s]=wg); // make `wg` available from `win[s]`
('name' in wg) || (wg.name=s); // and set `wg.name` to `s`.
}
if( !wg.helpTip && (t=host.helpTip) ) // [180825] Inherits `helpTip`
{ // from `host` if not assigned...
false===x.helpTip || (wg.helpTip=t); // [210711] ...unless `x.helpTip===false`
}
if( !ISWG ) continue; // If `wg` is a true SUI widget:
callee.call(this,x,wg); // 1. Recurse (must be done first.)
callee.WSTA.INIT(wg); // 2. Watch states (visible,enabled) [190310]
}
// [190120] Cleanup `res` keys?
// ---
if( RS ) for( k in res ) res.hasOwnProperty(k) && delete res[k];
return host;
}
.setup
({
// Properties inspected at creation time.
// ---
RE_CREA: RegExp('^(?:' + 'properties|factory' + ')$'),
// Reverse 'stack' in Win < CC.
// ---
REV_STACK: $$.inCC ? 0 : $$.inWin,
#include 'builder/$$.RVRS.jsxres'
// Internal routines.
// ---
#include 'builder/$$.WIND.jsxres'
#include 'builder/$$.ARGS.jsxres'
#include 'builder/$$.SETK.jsxres'
// --- Widget creation
#include 'builder/$$.CUST.jsxres'
#include 'builder/$$.WIDG.jsxres'
// --- States
#include 'builder/$$.WSTA.jsxres'
});
//==========================================================================
// [190310] ScriptUI.isHidden() .isDisabled()
//==========================================================================
ScriptUI.isHidden = function isHidden(/*Widget*/wg,/*bool=0*/explicit)
//----------------------------------
// Whether the widget state matches HIDDEN, assuming `wg` has a `__state__`
// property. By default, non-visibility is asserted from both explicit flag
// or inherited state. Make `explicit` truthy to request the `h` flag alone.
// - explicit==0 => Whether the widget is hidden for whatever reason ; `Hh!=0`
// - explicit!=0 => Whether the widget is hidden in person ; `h==1`
// ---
// If `wg` has no __state__ property, use +(!wg.visible) as a fallback.
// ---
// => 1 [HIDDEN] | 0 [NOT-HIDDEN]
{
return wg.hasOwnProperty('__state__') ?
( ((explicit?1:3)&wg.__state__) && 1 ) :
+(!wg.visible);
};
ScriptUI.isDisabled = function isDisabled(/*Widget*/wg,/*bool=0*/explicit)
//----------------------------------
// Whether the widget state matches DISABLED, assuming `wg` has a `__state__`
// property. By default, non-enability is asserted from both explicit flag
// or implied/inherited state. Make `explicit` truthy to request the `d` flag alone.
// - explicit==0 => Whether the widget is disabled for whatever reason ; `DdHh!=0`
// Note that HIDDEN implies DISABLED!
// - explicit!=0 => Whether the widget is disabled in person ; `d==1`
// ---
// If `wg` has no __state__ property, use +(!wg.enabled) as a fallback.
// ---
// => 1 [HIDDEN] | 0 [NOT-HIDDEN]
{
return wg.hasOwnProperty('__state__') ?
( ((explicit?4:15)&wg.__state__) && 1 ) :
+(!wg.enabled);
};
//==========================================================================
// [181201] ScriptUI.addKeyHandler() .removeKeyHandler()
//==========================================================================
ScriptUI.addKeyHandler = function addKeyHandler(/*key|key[]*/k,/*fct*/handler, i,t)
//----------------------------------
// Add a "key handler" to ScriptUI.builder. Whenever the key `k`
// is encountered in the resource object (`k : val` assignments),
// the code `<widget>[k]=val` is replaced by
// handler.call( <context>, <widget>, val, k ).
// The `handler` function must support at least two parameters:
// 1. (obj) The target widget or object.
// 2. (any) The value to be assigned.
// (The key `k` itself is passed to the handler as 3rd parameter.)
// [CHG210710] The argument `k` can be an array of keys as well,
// so every key is associated to the same handler.
// [REM] Cf. `ScriptUI.builder.SETK.EXTERN`
// ---
// => callee
{
'function'==typeof handler
|| error(__("%1 > Invalid handler (%2). Should be a function.",callee.name,handler && typeof handler));
// [CHG210710] Array support.
// ---
( k && k instanceof Array ) || (k=[k]);
for( i=-1 ; ++i < k.length ; )
{
'string'==typeof(t=k[i])
|| error(__("%1 > Invalid key (%2). Should be a string.",callee.name,t && typeof t));
ScriptUI.builder.SETK.EXTERN[t] = handler;
}
return callee;
};
ScriptUI.removeKeyHandler = function removeKeyHandler(/*key*/k)
//----------------------------------
// Remove a "key handler" from ScriptUI.builder (in case it is no
// longer needed.)
// => undefined
{
'string'==typeof k
|| error(__("%1 > Invalid key (%2). Should be a string.",callee.name,k && typeof k));
delete ScriptUI.builder.SETK.EXTERN[k];
};