@usman404/crowjs 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +1 -0
- package/Core/Component.js +426 -0
- package/Core/GUIEvent/GUIEvent.js +23 -0
- package/Core/GUIEvent/KeyboardEvent.js +14 -0
- package/Core/GUIEvent/MouseEvent.js +22 -0
- package/Core/Root.js +558 -0
- package/Frames/DummyFrame.js +185 -0
- package/Frames/Frame.js +531 -0
- package/Frames/FrameComponent.js +54 -0
- package/Frames/GridFrame.js +574 -0
- package/Frames/ScrollFrame.js +764 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/UIComponents/Input.js +78 -0
- package/UIComponents/Label.js +234 -0
- package/UIComponents/TextField.js +551 -0
- package/UIComponents/UIComponent.js +97 -0
- package/crowjs-01-01.png +0 -0
- package/index.html +15 -0
- package/package.json +23 -0
- package/sketch.js +65 -0
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
import {Frame} from './Frame.js';
|
|
2
|
+
|
|
3
|
+
export class ScrollFrame extends Frame{
|
|
4
|
+
/**
|
|
5
|
+
* Creates a scrollable container for child components
|
|
6
|
+
* @param {number} x - The x-coordinate
|
|
7
|
+
* @param {number} y - The y-coordinate
|
|
8
|
+
* @param {number} width - The width
|
|
9
|
+
* @param {number} height - The height
|
|
10
|
+
* @param {Object} options - Configuration options
|
|
11
|
+
* @param {string|null} options.id - Component ID
|
|
12
|
+
* @param {p5.Color} options.backgroundColor - Background color
|
|
13
|
+
* @param {p5.Color} options.borderColor - Border color
|
|
14
|
+
* @param {p5.Color} options.highlightedBorderColor - Highlighted border color
|
|
15
|
+
* @param {number} options.borderWidth - Border width
|
|
16
|
+
* @param {number} options.cornerRadius - Corner radius
|
|
17
|
+
* @param {number} options.padx - Horizontal padding
|
|
18
|
+
* @param {number} options.pady - Vertical padding
|
|
19
|
+
* @param {boolean} options.alwaysShowBanner - Always show banner
|
|
20
|
+
* @param {boolean} options.enableVScroll - Enable vertical scrolling
|
|
21
|
+
* @param {boolean} options.enableHScroll - Enable horizontal scrolling
|
|
22
|
+
* @param {number} options.scrollSensitivity - Scroll speed
|
|
23
|
+
* @param {number} options.bannerHeight - Banner height
|
|
24
|
+
* @param {string} options.alignment - Layout alignment ("v" or "h")
|
|
25
|
+
* @param {number} options.nearestBorderThreshold - Border detection threshold
|
|
26
|
+
* @param {Component|null} options.parent - Parent component
|
|
27
|
+
* @param {boolean} options.enableReposition - Allow dragging
|
|
28
|
+
* @param {boolean} options.enableOptimisedReposition - Optimized repositioning
|
|
29
|
+
* @param {boolean} options.enableResizing - Allow resizing
|
|
30
|
+
* @param {boolean} options.enableOptimisedResizing - Optimized resizing
|
|
31
|
+
* @param {boolean} options.enableShadow - Enable shadow
|
|
32
|
+
* @param {string} options.shadowColor - Shadow color
|
|
33
|
+
* @param {number} options.shadowIntensity - Shadow opacity
|
|
34
|
+
* @param {number} options.shadowSpread - Shadow spread
|
|
35
|
+
* @param {number} options.shadowDetail - Shadow layers
|
|
36
|
+
*/
|
|
37
|
+
constructor(x, y, width, height, {
|
|
38
|
+
id=null,
|
|
39
|
+
backgroundColor = color(255),
|
|
40
|
+
borderColor = color(0),
|
|
41
|
+
highlightedBorderColor = color(0),
|
|
42
|
+
borderWidth = 1,
|
|
43
|
+
cornerRadius = 0,
|
|
44
|
+
padx=0,
|
|
45
|
+
pady=0,
|
|
46
|
+
alwaysShowBanner = false,
|
|
47
|
+
enableVScroll=false,
|
|
48
|
+
enableHScroll=false,
|
|
49
|
+
scrollSensitivity=20,
|
|
50
|
+
bannerHeight=35,
|
|
51
|
+
alignment="v", //v for vertical, h for horizontal
|
|
52
|
+
nearestBorderThreshold=8,
|
|
53
|
+
parent=null,
|
|
54
|
+
enableReposition=false,
|
|
55
|
+
enableOptimisedReposition=false,
|
|
56
|
+
enableResizing=false,
|
|
57
|
+
enableOptimisedResizing=false,
|
|
58
|
+
enableShadow=false,
|
|
59
|
+
shadowColor= 'rgb(0,0,0)',
|
|
60
|
+
shadowIntensity= 0.3,
|
|
61
|
+
shadowSpread= 1,
|
|
62
|
+
shadowDetail=10,
|
|
63
|
+
} = {}) {
|
|
64
|
+
bannerHeight = bannerHeight%height;
|
|
65
|
+
super(x, y, width, height, id, backgroundColor, borderColor, highlightedBorderColor, borderWidth,
|
|
66
|
+
cornerRadius, padx, pady, alwaysShowBanner, bannerHeight, nearestBorderThreshold, parent, "Frame",
|
|
67
|
+
enableReposition, enableOptimisedReposition, enableResizing, enableOptimisedResizing, enableShadow, shadowColor, shadowIntensity, shadowSpread, shadowDetail);
|
|
68
|
+
|
|
69
|
+
this.preferences = [];
|
|
70
|
+
//used for calculating weighted dimensions of child elements
|
|
71
|
+
this.totalWeight = 0;
|
|
72
|
+
//flags for scroll on/off
|
|
73
|
+
this.enableVScroll=enableVScroll;
|
|
74
|
+
this.enableHScroll=enableHScroll;
|
|
75
|
+
//for internal alignment of elements
|
|
76
|
+
this.alignment = alignment;
|
|
77
|
+
|
|
78
|
+
if(this.alignment=="vertical"){
|
|
79
|
+
this.alignment="v";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if(this.alignment=="horizontal"){
|
|
83
|
+
this.alignment="h";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if(this.alignment!="v" && this.alignment!="h"){
|
|
87
|
+
this.alignment="v";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//sets the amount in % to scroll in any direction
|
|
91
|
+
if(this.enableHScroll || this.enableVScroll){
|
|
92
|
+
this.scrollSensitivity=scrollSensitivity;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if(this.enableVScroll){
|
|
96
|
+
this.topper=-1;
|
|
97
|
+
this.deepest=-1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if(this.enableHScroll){
|
|
101
|
+
this.leftist=-1;
|
|
102
|
+
this.rightist=-1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//default event listeners
|
|
106
|
+
this.addEventListener("keyDown", (event) => this.onKeyDown(event));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Handles keyboard navigation for scrolling
|
|
111
|
+
* @param {KeyboardEvent} event - The key down event
|
|
112
|
+
*/
|
|
113
|
+
onKeyDown(event) {
|
|
114
|
+
// console.log("key is pressed...");
|
|
115
|
+
if(keyIsDown(LEFT_ARROW)){
|
|
116
|
+
this.scrollLeft();
|
|
117
|
+
} else if(keyIsDown(RIGHT_ARROW)){
|
|
118
|
+
this.scrollRight();
|
|
119
|
+
} else if(keyIsDown(UP_ARROW)){
|
|
120
|
+
this.scrollUp();
|
|
121
|
+
} else if(keyIsDown(DOWN_ARROW)){
|
|
122
|
+
this.scrollDown();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Renders the scroll frame and child components
|
|
128
|
+
*/
|
|
129
|
+
show() {
|
|
130
|
+
//shadow
|
|
131
|
+
if(this.enableShadow){
|
|
132
|
+
this.drawShadow();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//coloring the background
|
|
136
|
+
if(this.backgroundColor!=null){
|
|
137
|
+
push();
|
|
138
|
+
noStroke();
|
|
139
|
+
fill(this.backgroundColor);
|
|
140
|
+
rect(this.x, this.y, this.width, this.height, this.cornerRadius);
|
|
141
|
+
pop();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//applying clipping mask
|
|
145
|
+
push();
|
|
146
|
+
beginClip();
|
|
147
|
+
rect(this.x, this.y, this.width, this.height, this.cornerRadius);
|
|
148
|
+
endClip();
|
|
149
|
+
|
|
150
|
+
//displaying child elements
|
|
151
|
+
for (let child of this.children) {
|
|
152
|
+
child.show();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
//show the top banner
|
|
156
|
+
if(this.alwaysShowBanner || (this.enableReposition && this.isBannerShown)){
|
|
157
|
+
noStroke();
|
|
158
|
+
fill(0);
|
|
159
|
+
rect(this.x, this.y, this.width, this.bannerHeight);
|
|
160
|
+
|
|
161
|
+
fill(255);
|
|
162
|
+
ellipse(this.x+this.width/2, this.y+(this.bannerHeight)/2, (this.bannerHeight)/4, (this.bannerHeight)/4);
|
|
163
|
+
ellipse(this.x+this.width/2 - (this.bannerHeight)/2, this.y+(this.bannerHeight)/2, (this.bannerHeight)/4, (this.bannerHeight)/4);
|
|
164
|
+
ellipse(this.x+this.width/2 + (this.bannerHeight)/2, this.y+(this.bannerHeight)/2, (this.bannerHeight)/4, (this.bannerHeight)/4);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
pop();
|
|
168
|
+
|
|
169
|
+
//displaying the outline/border
|
|
170
|
+
if(this.borderColor!=null){
|
|
171
|
+
push();
|
|
172
|
+
stroke(this.borderColor);
|
|
173
|
+
strokeWeight(this.borderWidth);
|
|
174
|
+
noFill();
|
|
175
|
+
rect(this.x, this.y, this.width, this.height, this.cornerRadius);
|
|
176
|
+
pop();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//highlight the relevant border if cursor is sufficiently near to it
|
|
180
|
+
// if(this.enableResizing && this.nearestBorder!=null){
|
|
181
|
+
// this.showHighlightedBorder();
|
|
182
|
+
// }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Adds a component to the scroll frame with weighted sizing
|
|
187
|
+
* @param {Component} element - The component to add
|
|
188
|
+
* @param {Object} options - Placement options
|
|
189
|
+
* @param {number} options.weight - Proportional weight for sizing
|
|
190
|
+
* @param {number} options.padL - Left padding
|
|
191
|
+
* @param {number} options.padR - Right padding
|
|
192
|
+
* @param {number} options.padT - Top padding
|
|
193
|
+
* @param {number} options.padB - Bottom padding
|
|
194
|
+
*/
|
|
195
|
+
add(element,
|
|
196
|
+
{
|
|
197
|
+
weight=1,
|
|
198
|
+
padL=0,
|
|
199
|
+
padR=0,
|
|
200
|
+
padT=0,
|
|
201
|
+
padB=0
|
|
202
|
+
}={})
|
|
203
|
+
{
|
|
204
|
+
if(element==null){
|
|
205
|
+
console.log("element to add can't be null");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if(this.findElement(element)){
|
|
210
|
+
console.log(`the component (id: ${element.id}) is already added to the scrollframe (${this.id})`);
|
|
211
|
+
console.log("component: ", element, "\nScrollFrame: ", this);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if(weight<=0){
|
|
216
|
+
console.log("weight can't be non-positive");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if(this.getElementById(element.id)){
|
|
221
|
+
console.log(`component with duplicate id (${element.id}) found in ${this.constructor.name}; component (${element.constructor.name}) can't be added!`);
|
|
222
|
+
console.log(this);
|
|
223
|
+
console.log("");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
element.turnResizingAndRepositionOff();
|
|
228
|
+
element.parent=this;
|
|
229
|
+
this.children.push(element);
|
|
230
|
+
// 0 , 1 , 2 , 3 , 4 , 5
|
|
231
|
+
//old: element, weight, padL, padR, padT, padB
|
|
232
|
+
// 0 , 1 , 2 , 3 , 4
|
|
233
|
+
//new: weight, padL, padR, padT, padB
|
|
234
|
+
this.preferences.push([weight, padL, padR, padT, padB]);
|
|
235
|
+
this.totalWeight+=weight;
|
|
236
|
+
this.redraw();
|
|
237
|
+
|
|
238
|
+
if(this.enableHScroll==true){
|
|
239
|
+
this.findLeftist();
|
|
240
|
+
this.findRightist();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if(this.enableVScroll==true){
|
|
244
|
+
this.findTopper();
|
|
245
|
+
this.findDeepest();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Removes a component from the scroll frame
|
|
251
|
+
* @param {Component} element - The component to remove
|
|
252
|
+
*/
|
|
253
|
+
remove(element){
|
|
254
|
+
let index = this.findIndexOfElement(element);
|
|
255
|
+
if(index==-1){
|
|
256
|
+
console.log(`element (id: ${element.id}) can't be removed from ScrollFrame (id: ${this.id})
|
|
257
|
+
because it was not found in immediate children!`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this.children[index].parent = null;
|
|
262
|
+
this.totalWeight -= this.preferences[index][0];
|
|
263
|
+
this.preferences = this.preferences.filter((_, i) => i!==index);
|
|
264
|
+
this.removeChild(element);
|
|
265
|
+
this.redraw();
|
|
266
|
+
|
|
267
|
+
console.log(`element (id: ${element.id}) successfully removed from ${this.constructor.name} (id: ${this.id})!`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Sets the weight of a child component
|
|
272
|
+
* @param {number} index - Child index
|
|
273
|
+
* @param {number} weight - New weight value
|
|
274
|
+
*/
|
|
275
|
+
setWeight(index, weight){
|
|
276
|
+
if(index<0 || index>this.children.length-1){
|
|
277
|
+
console.log("index out of range! can't set new weight");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if(weight<1){
|
|
282
|
+
console.log("weight to set must be >= 1");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.totalWeight -= this.preferences[index][0];
|
|
287
|
+
this.totalWeight += weight;
|
|
288
|
+
this.preferences[index][0] = weight;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Gets the weight of a child component
|
|
293
|
+
* @param {number} index - Child index
|
|
294
|
+
* @returns {number} The weight value
|
|
295
|
+
*/
|
|
296
|
+
getWeight(index){
|
|
297
|
+
if(index<0 || index>this.children.length-1){
|
|
298
|
+
console.log("index out of range! can't set new weight");
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return this.preferences[index][0];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Sets left padding for a child component
|
|
307
|
+
* @param {number} index - Child index
|
|
308
|
+
* @param {number} padL - Left padding value
|
|
309
|
+
*/
|
|
310
|
+
setPadL(index, padL){
|
|
311
|
+
if(index<0 || index>this.children.length-1){
|
|
312
|
+
console.log("index out of range! can't set new padL");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if(padL<0){
|
|
317
|
+
console.log("padL to set must be >= 0");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
this.preferences[index][1] = padL;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Gets left padding of a child component
|
|
326
|
+
* @param {number} index - Child index
|
|
327
|
+
* @returns {number} Left padding value
|
|
328
|
+
*/
|
|
329
|
+
getPadL(index){
|
|
330
|
+
if(index<0 || index>this.children.length-1){
|
|
331
|
+
console.log("index out of range! can't set new padL");
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return this.preferences[index][1];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Sets right padding for a child component
|
|
340
|
+
* @param {number} index - Child index
|
|
341
|
+
* @param {number} padR - Right padding value
|
|
342
|
+
*/
|
|
343
|
+
setPadR(index, padR){
|
|
344
|
+
if(index<0 || index>this.children.length-1){
|
|
345
|
+
console.log("index out of range! can't set new padR");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if(padR<0){
|
|
350
|
+
console.log("padR to set must be >= 0");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this.preferences[index][2] = padR;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Gets right padding of a child component
|
|
359
|
+
* @param {number} index - Child index
|
|
360
|
+
* @returns {number} Right padding value
|
|
361
|
+
*/
|
|
362
|
+
getPadR(index){
|
|
363
|
+
if(index<0 || index>this.children.length-1){
|
|
364
|
+
console.log("index out of range! can't set new padR");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return this.preferences[index][2];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Sets top padding for a child component
|
|
373
|
+
* @param {number} index - Child index
|
|
374
|
+
* @param {number} padT - Top padding value
|
|
375
|
+
*/
|
|
376
|
+
setPadT(index, padT){
|
|
377
|
+
if(index<0 || index>this.children.length-1){
|
|
378
|
+
console.log("index out of range! can't set new padT");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if(padT<0){
|
|
383
|
+
console.log("padT to set must be >= 0");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
this.preferences[index][3] = padT;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Gets top padding of a child component
|
|
392
|
+
* @param {number} index - Child index
|
|
393
|
+
* @returns {number} Top padding value
|
|
394
|
+
*/
|
|
395
|
+
getPadT(index){
|
|
396
|
+
if(index<0 || index>this.children.length-1){
|
|
397
|
+
console.log("index out of range! can't set new padT");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return this.preferences[index][3];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Sets bottom padding for a child component
|
|
406
|
+
* @param {number} index - Child index
|
|
407
|
+
* @param {number} padB - Bottom padding value
|
|
408
|
+
*/
|
|
409
|
+
setPadB(index, padB){
|
|
410
|
+
if(index<0 || index>this.children.length-1){
|
|
411
|
+
console.log("index out of range! can't set new padB");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if(padB<0){
|
|
416
|
+
console.log("padB to set must be >= 0");
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this.preferences[index][4] = padB;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Gets bottom padding of a child component
|
|
425
|
+
* @param {number} index - Child index
|
|
426
|
+
* @returns {number} Bottom padding value
|
|
427
|
+
*/
|
|
428
|
+
getPadB(index){
|
|
429
|
+
if(index<0 || index>this.children.length-1){
|
|
430
|
+
console.log("index out of range! can't set new padB");
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return this.preferences[index][4];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
//the following find methods find the relevant subjects by maintaing a
|
|
438
|
+
//reference variable. This variable is conditionally updated by comparing
|
|
439
|
+
//it to the most recently added element in the list. Therefore, there is
|
|
440
|
+
//no need to loop over all the elements every single time any element is added.
|
|
441
|
+
|
|
442
|
+
//finds the element nearest to the top
|
|
443
|
+
/**
|
|
444
|
+
* Finds the topmost child component
|
|
445
|
+
*/
|
|
446
|
+
findTopper(){
|
|
447
|
+
if(this.topper==-1){
|
|
448
|
+
this.topper=0;
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
let i = this.preferences.length-1;
|
|
453
|
+
if(this.children[i].y +
|
|
454
|
+
this.preferences[i][3] <
|
|
455
|
+
this.children[this.topper].y +
|
|
456
|
+
this.preferences[this.topper][3]){
|
|
457
|
+
this.topper = i;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
//finds the element that is farther down than any other element
|
|
462
|
+
/**
|
|
463
|
+
* Finds the bottommost child component
|
|
464
|
+
*/
|
|
465
|
+
|
|
466
|
+
findDeepest(){
|
|
467
|
+
if(this.deepest==-1){
|
|
468
|
+
this.deepest=0;
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let i = this.preferences.length-1;
|
|
473
|
+
if(this.children[i].y +
|
|
474
|
+
this.children[i].height -
|
|
475
|
+
this.preferences[i][4] >
|
|
476
|
+
this.children[this.deepest].y +
|
|
477
|
+
this.children[this.deepest].height -
|
|
478
|
+
this.preferences[this.deepest][4]){
|
|
479
|
+
this.deepest = i;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
//finds the element nearest to the left boundary
|
|
484
|
+
/**
|
|
485
|
+
* Finds the leftmost child component
|
|
486
|
+
*/
|
|
487
|
+
findLeftist(){
|
|
488
|
+
if(this.leftist==-1){
|
|
489
|
+
this.leftist=0;
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
let i = this.preferences.length-1;
|
|
494
|
+
if(this.children[i].x +
|
|
495
|
+
this.preferences[i][1] <
|
|
496
|
+
this.children[this.leftist].x +
|
|
497
|
+
this.preferences[this.leftist][1]){
|
|
498
|
+
this.leftist = i
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
//finds the element farthest to the right
|
|
503
|
+
/**
|
|
504
|
+
* Finds the rightmost child component
|
|
505
|
+
*/
|
|
506
|
+
findRightist(){
|
|
507
|
+
if(this.rightist==-1){
|
|
508
|
+
this.rightist=0;
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
let i = this.preferences.length-1;
|
|
513
|
+
if(this.children[i].x +
|
|
514
|
+
this.children[i].width -
|
|
515
|
+
this.preferences[i][2] >
|
|
516
|
+
this.children[this.rightist].x +
|
|
517
|
+
this.children[this.rightist].width -
|
|
518
|
+
this.preferences[this.rightist][2]){
|
|
519
|
+
this.rightist = i;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Scrolls content downward
|
|
524
|
+
*/
|
|
525
|
+
scrollDown(){
|
|
526
|
+
if(!this.enableVScroll || this.children.length==0){
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if(this.alignment=="v"){
|
|
531
|
+
let last_elem = this.children[this.children.length-1];
|
|
532
|
+
if(last_elem.y + last_elem.height > this.y + this.height - this.pady - this.preferences[this.preferences.length-1][4]){
|
|
533
|
+
this.vScrollUtil(-1);
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
//need to find the one that extends the longest
|
|
537
|
+
if(this.children[this.deepest].y + this.children[this.deepest].height > this.y + this.height - this.pady- this.preferences[this.deepest][4]){
|
|
538
|
+
this.vScrollUtil(-1);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Scrolls content upward
|
|
544
|
+
*/
|
|
545
|
+
scrollUp(){
|
|
546
|
+
if(!this.enableVScroll || this.children.length==0){
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if(this.alignment=="v"){
|
|
551
|
+
let first_elem = this.children[0];
|
|
552
|
+
if(first_elem.y + this.preferences[0][3] <this.y + this.pady){
|
|
553
|
+
this.vScrollUtil(1);
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
//need to find the element that is nearest to the top
|
|
557
|
+
if(this.children[this.topper].y + this.preferences[this.topper][3] <this.y + this.pady){
|
|
558
|
+
this.vScrollUtil(1);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Utility method for vertical scrolling
|
|
564
|
+
* @param {number} multiple - Scroll direction and multiplier
|
|
565
|
+
*/
|
|
566
|
+
vScrollUtil(multiple){
|
|
567
|
+
for(let i=0; i<this.children.length; i++){
|
|
568
|
+
let elem = this.children[i];
|
|
569
|
+
|
|
570
|
+
if(elem.constructor.name=="ScrollFrame"){
|
|
571
|
+
elem.vScrollUtil(multiple);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
elem.y+=multiple*this.scrollSensitivity;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Scrolls content to the left
|
|
579
|
+
*/
|
|
580
|
+
scrollLeft(){
|
|
581
|
+
if(!this.enableHScroll || this.children.length==0){
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if(this.alignment=="v"){
|
|
586
|
+
//do something about the loop
|
|
587
|
+
if(this.children[this.leftist].x-this.preferences[this.leftist][1]<this.x+this.padx){
|
|
588
|
+
this.hScrollUtil(1);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
} else {
|
|
592
|
+
//first element
|
|
593
|
+
if(this.children[0].x-this.preferences[0][1]<this.x+this.padx){
|
|
594
|
+
this.hScrollUtil(1);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Scrolls content to the right
|
|
600
|
+
*/
|
|
601
|
+
scrollRight(){
|
|
602
|
+
if(!this.enableHScroll || this.children.length==0){
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if(this.alignment=="v"){
|
|
607
|
+
if(this.children[this.rightist].x+this.children[this.rightist].width+this.preferences[this.rightist][2]>this.x+this.width-this.padx){
|
|
608
|
+
this.hScrollUtil(-1);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
} else {
|
|
612
|
+
let last_elem = this.children[this.children.length-1];
|
|
613
|
+
if(last_elem.x+last_elem.width+this.preferences[this.preferences.length-1][2]>this.x+this.width-this.padx){
|
|
614
|
+
this.hScrollUtil(-1);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Utility method for horizontal scrolling
|
|
620
|
+
* @param {number} multiple - Scroll direction and multiplier
|
|
621
|
+
*/
|
|
622
|
+
hScrollUtil(multiple){
|
|
623
|
+
for(let i=0; i<this.children.length; i++){
|
|
624
|
+
let elem = this.children[i];
|
|
625
|
+
|
|
626
|
+
if(elem.constructor.name=="ScrollFrame"){
|
|
627
|
+
elem.hScrollUtil(multiple);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
elem.x+=multiple*this.scrollSensitivity;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Shows banner and adjusts content position
|
|
635
|
+
*/
|
|
636
|
+
showBanner(){
|
|
637
|
+
if(this.enableVScroll==true){
|
|
638
|
+
this.BannerUtil(1, this.bannerHeight);
|
|
639
|
+
} else {
|
|
640
|
+
this.adjustHeight(this.y + (this.bannerHeight) + this.pady, this.height - (this.bannerHeight) - 2*(this.pady));
|
|
641
|
+
}
|
|
642
|
+
this.isBannerShown=true;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Hides banner and adjusts content position
|
|
646
|
+
*/
|
|
647
|
+
hideBanner(){
|
|
648
|
+
if(this.enableReposition && this.isBannerShown){
|
|
649
|
+
if(this.enableVScroll==true){
|
|
650
|
+
this.BannerUtil(-1, this.bannerHeight);
|
|
651
|
+
} else {
|
|
652
|
+
this.adjustHeight(this.y + this.pady, this.height-2*(this.pady));
|
|
653
|
+
}
|
|
654
|
+
this.isBannerShown=false;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Utility for banner visibility changes
|
|
659
|
+
* @param {number} dir - Direction (1 for show, -1 for hide)
|
|
660
|
+
* @param {number} heightAdjustment - Height adjustment amount
|
|
661
|
+
*/
|
|
662
|
+
BannerUtil(dir, heightAdjustment){
|
|
663
|
+
for(let i=0; i<this.children.length; i++){
|
|
664
|
+
let curr = this.children[i];
|
|
665
|
+
curr.y += dir*heightAdjustment;
|
|
666
|
+
|
|
667
|
+
if(curr.constructor.name=="ScrollFrame"){
|
|
668
|
+
curr.BannerUtil(dir, heightAdjustment);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Adjusts child component heights based on weights and alignment
|
|
674
|
+
* @param {number} y - Starting y position
|
|
675
|
+
* @param {number} h - Available height
|
|
676
|
+
*/
|
|
677
|
+
adjustHeight(y, h){
|
|
678
|
+
//[element, weight, padL, padR, padT, padB]
|
|
679
|
+
for(let i=0; i<this.children.length; i++){
|
|
680
|
+
|
|
681
|
+
let curr = this.children[i];
|
|
682
|
+
|
|
683
|
+
if(i-1>=0){
|
|
684
|
+
let prev = this.children[i-1];
|
|
685
|
+
if(this.alignment=="v"){
|
|
686
|
+
curr.y = prev.y + prev.height + this.preferences[i-1][4] + this.preferences[i][3];
|
|
687
|
+
} else {
|
|
688
|
+
curr.y = y + this.preferences[i][3];
|
|
689
|
+
}
|
|
690
|
+
} else {
|
|
691
|
+
curr.y = y + this.preferences[i][3];
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if(this.enableVScroll==false){
|
|
695
|
+
if(this.alignment=="v"){
|
|
696
|
+
curr.height = (this.preferences[i][0]/(this.totalWeight))*(h) - this.preferences[i][3] - this.preferences[i][4];
|
|
697
|
+
} else {
|
|
698
|
+
curr.height = h - this.preferences[i][3] - this.preferences[i][4];
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if(curr.type=="Frame"){
|
|
703
|
+
curr.adjustHeight(curr.y + curr.pady, curr.height - 2*(curr.pady));
|
|
704
|
+
} else {
|
|
705
|
+
if(this.enableVScroll==false){
|
|
706
|
+
curr.updateHeight();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Adjusts child component widths based on weights and alignment
|
|
714
|
+
* @param {number} x - Starting x position
|
|
715
|
+
* @param {number} w - Available width
|
|
716
|
+
*/
|
|
717
|
+
adjustWidth(x, w){
|
|
718
|
+
//[element, weight, padL, padR, padT, padB]
|
|
719
|
+
for(let i=0; i<this.children.length; i++){
|
|
720
|
+
let curr = this.children[i];
|
|
721
|
+
if(i-1>=0){
|
|
722
|
+
let prev = this.children[i-1];
|
|
723
|
+
if(this.alignment!="v"){
|
|
724
|
+
curr.x = prev.x + prev.width + this.preferences[i-1][2] + this.preferences[i][1];
|
|
725
|
+
} else {
|
|
726
|
+
curr.x = x + this.preferences[i][1];
|
|
727
|
+
}
|
|
728
|
+
} else {
|
|
729
|
+
curr.x = x + this.preferences[i][1];
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if(this.enableHScroll==false){
|
|
733
|
+
if(this.alignment=="v"){
|
|
734
|
+
curr.width = w - this.preferences[i][1] - this.preferences[i][2];
|
|
735
|
+
} else {
|
|
736
|
+
curr.width = (this.preferences[i][0]/(this.totalWeight))*(w) - this.preferences[i][1] - this.preferences[i][2];
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if(curr.type=="Frame"){
|
|
741
|
+
curr.adjustWidth(curr.x + curr.padx, curr.width - 2*(curr.padx));
|
|
742
|
+
} else {
|
|
743
|
+
if(this.enableHScroll==false){
|
|
744
|
+
curr.updateWidth();
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Updates child positions during frame movement
|
|
752
|
+
* @param {number} xDiff - X position difference
|
|
753
|
+
* @param {number} yDiff - Y position difference
|
|
754
|
+
*/
|
|
755
|
+
updatePosUtil(xDiff, yDiff){
|
|
756
|
+
for(let i=0; i<this.children.length; i++){
|
|
757
|
+
this.children[i].x -= xDiff;
|
|
758
|
+
this.children[i].y -= yDiff;
|
|
759
|
+
if(this.children[i].type=="Frame"){
|
|
760
|
+
this.children[i].updatePosUtil(xDiff, yDiff);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|