forked from livecode/livecode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtreeview.lcb
More file actions
1669 lines (1345 loc) · 52.6 KB
/
Copy pathtreeview.lcb
File metadata and controls
1669 lines (1345 loc) · 52.6 KB
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
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright (C) 2015 Runtime Revolution Ltd.
This file is part of LiveCode.
LiveCode is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free
Software Foundation.
LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
/*
A widget to display array data in a tree view
Name: selectedElementChanged
Type: message
Syntax: selectedElementChanged pPath
Summary: Sent when an element is selected
Parameters:
pPath: The path to the selected element
Description:
The selectedElementChanged message is sent to the widget's script object when a row of the widget's display is clicked on,
causing that row to be selected. The <pPath> parameter contains the path to the new <selectedElement>.
References: selectedElement (property)
*/
widget com.livecode.widget.treeView
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
metadata author is "LiveCode"
metadata version is "1.0.0"
metadata title is "Tree View"
metadata userVisible is "false"
-- property declarations
/*
Syntax: set the arrayData of <widget> to <pArray>
Syntax: get the arrayData of <widget>
Summary: The array being displayed by the widget
Parameters:
pArray (array): The array data.
Description:
The arrayData is the data currently being displayed by the tree view widget.
*/
property arrayData get getArrayData set setArrayData
/*
Syntax: set the alternateRowBackgrounds of <widget> to {true|false}
Syntax: get the alternateRowBackgrounds of <widget>
Summary: Whether the alternate rows of the widget have different backgrounds or not.
Description:
Use the alternateRowBackgrounds property if you want to more clearly distinguish the rows displayed by the widget.
*/
property alternateRowBackgrounds get mAlternateRowBackgrounds set setRowBackgrounds
/*
Syntax: set the selectedElement of <widget> to <pPath>
Syntax: get the selectedElement of <widget>
Summary: Select the row corresponding to <pPath>
Parameters:
pPath: A comma delimited list of array keys.
Description:
<pPath> is a list of the keys which determine the row to be selected. For example, if tArray is the arrayData
of the widget, to select a row corresponding to tArray["key1"]["subkey2"]["subsubkey5"], simply execute
``` set the selectedElement of widget "Array Viewer" to "key1,subkey2,subsubkey5" ```
*/
property selectedElement get getSelectedElement set setSelectedElement
/*
Syntax: set the showFrameBorder of <widget> to {true|false}
Syntax: get the showFrameBorder of <widget>
Summary: Whether the widget has a border or not.
Description:
Use the showFrameBorder property to show the bounds of the widget object.
*/
property showFrameBorder get mFrameBorder set setFrameBorder
/*
Syntax: set the selectedRowColor of <widget> to <pColor>
Syntax: get the selectedRowColor of <widget>
Summary: The color used to highlight a selected row.
Parameters:
pColor: The color to use to highlight a row when it is selected.
Description:
Use the <selectedRowColor> property to get or set the color the tree view widget uses to highlight the selected row.
*/
property selectedRowColor get mSelectedRowColor set setSelectedRowColor
metadata selectedRowColor.editor is "com.livecode.pi.color"
metadata selectedRowColor.default is "10,105,216"
/*
Syntax: set the readOnly of <widget> to {true|false}
Syntax: get the readOnly of <widget>
Summary: Whether the options to modify elements of the underlying array are present or not.
Description:
The <readOnly> property controls whether the widget presents the option to add elements to arrays or not.
If false, the first row of the widget is always "Add new element", and when rows are hovered over, icons
appear at the right to enable the removal of that element, or the addition of a new subelement.
*/
property readOnly get mReadOnly set setReadOnly
/*
Syntax: set the arrayStyle of <widget> to {true|false}
Syntax: get the arrayStyle of <widget>
Summary: Whether the tree view should display its contents in array style or as a standard tree view.
Description:
The <arrayStyle> property controls whether the keys of the <arrayData> of the widget are displayed with
square brackets around them or not.
References: arrayData (property)
*/
property arrayStyle get mArrayStyle set setArrayStyle
/*
Syntax: set the sortOrder of <widget> to <pOrder>
Syntax: get the sortOrder of <widget>
Summary: Manipulates the order in which elements of the tree view are displayed, with respect to the current <sortType>.
Parameters:
pOrder (enum): The order in which to display elements of the tree view.
-"ascending": Display from first to last in the order. This is the default
-"descending": Display from last to first in the order.
Description:
Use the <sortOrder> property to display the elements of the tree view in ascending or descending order of the
keys of its <arrayData>, given the current <sortType>.
References: arrayData (property), sortType (property)
*/
property sortOrder get getSortOrder set setSortOrder
metadata sortOrder.editor is "com.livecode.pi.enum"
metadata sortOrder.options is "ascending,descending"
/*
Syntax: set the sortType of <widget> to <pType>
Syntax: get the sortType of <widget>
Summary: Manipulates the type of ordering in which elements of the tree view are displayed.
Parameters:
pType (enum): The type of ordering to use in displaying elements of the tree view.
-"text": Display in alphabetical order of the keys
-"numeric": Display in numeric order of the keys. This is the default
Description:
Use the <sortType> property to use text or numeric comparison when ordering the keys of the <arrayData>
of the tree view widget.
References: sortOrder (property)
*/
property sortType get getSortType set setSortType
metadata sortType.editor is "com.livecode.pi.enum"
metadata sortType.options is "text,numeric"
// The unmodified array data
private variable mData as Array
// A flat list representing the array items to be displayed
private variable mDataList as List
// The number of lines to be displayed
private variable mDataCount as Integer
// The total height of the displayed data
private variable mDataHeight as Real
// The height of each row
private variable mRowHeight as Real
// The height of the view area
private variable mViewHeight as Real
// The width of the view area
private variable mViewWidth as Real
// The first part of the data being displayed
private variable mViewTopPosition as Real
// The index in the display list corresponding the the top of the view
private variable mFirstDataItem as Integer
private variable mMargin as Real
private variable mRecalculate as Boolean
private variable mAlternateRowBackgrounds as Boolean
private variable mSelectedRowColor as String
private variable mIndentPixels as Integer
private variable mHoverRow as Integer
private variable mHoverIcon as String
private variable mFrameBorder as Boolean
private variable mSelectedElement as optional List
private variable mFoldState as Array
private variable mFoldedArrowPath as Path
private variable mUnfoldedArrowPath as Path
private variable mDeleteItemPath as Path
private variable mAddItemPath as Path
private variable mIconRect as Rectangle
private variable mIconWidth as Real
private variable mReadOnly as Boolean
private variable mArrayStyle as Boolean
private variable mSortAscending as Boolean
private variable mSortNumeric as Boolean
constant kMaxKeyDisplayChars is 50
constant kMaxValueDisplayChars is 200
--------------------------------------------------------------------------------
--
-- Message handlers
--
--------------------------------------------------------------------------------
public handler OnCreate() returns nothing
put path "M0,7.421V0.21c0-0.168,0.188-0.268,0.327-0.174l5.351,3.605c0.124,0.083,0.124,0.265,0,0.348L0.327,7.595 C0.188,7.689,0,7.589,0,7.421z" into mFoldedArrowPath
put path "M0.221,0l7.565,0c0.177,0,0.281,0.188,0.183,0.327L4.186,5.679c-0.087,0.124-0.278,0.124-0.366,0L0.038,0.327 C-0.061,0.188,0.044,0,0.221,0z" into mUnfoldedArrowPath
put path "M512 736L512 1312Q512 1326 503 1335 494 1344 480 1344L416 1344Q402 1344 393 1335 384 1326 384 1312L384 736Q384 722 393 713 402 704 416 704L480 704Q494 704 503 713 512 722 512 736ZM768 736L768 1312Q768 1326 759 1335 750 1344 736 1344L672 1344Q658 1344 649 1335 640 1326 640 1312L640 736Q640 722 649 713 658 704 672 704L736 704Q750 704 759 713 768 722 768 736ZM1024 736L1024 1312Q1024 1326 1015 1335 1006 1344 992 1344L928 1344Q914 1344 905 1335 896 1326 896 1312L896 736Q896 722 905 713 914 704 928 704L992 704Q1006 704 1015 713 1024 722 1024 736ZM1152 1460L1152 512 256 512 256 1460Q256 1482 263 1500.5 270 1519 277.5 1527.5 285 1536 288 1536L1120 1536Q1123 1536 1130.5 1527.5 1138 1519 1145 1500.5 1152 1482 1152 1460ZM480 384L928 384 880 267Q873 258 863 256L546 256Q536 258 529 267ZM1408 416L1408 480Q1408 494 1399 503 1390 512 1376 512L1280 512 1280 1460Q1280 1543 1233 1603.5 1186 1664 1120 1664L288 1664Q222 1664 175 1605.5 128 1547 128 1464L128 512 32 512Q18 512 9 503 0 494 0 480L0 416Q0 402 9 393 18 384 32 384L341 384 411 217Q426 180 465 154 504 128 544 128L864 128Q904 128 943 154 982 180 997 217L1067 384 1376 384Q1390 384 1399 393 1408 402 1408 416Z" into mDeleteItemPath
put path "M1408 608L1408 800Q1408 840 1380 868 1352 896 1312 896L896 896 896 1312Q896 1352 868 1380 840 1408 800 1408L608 1408Q568 1408 540 1380 512 1352 512 1312L512 896 96 896Q56 896 28 868 0 840 0 800L0 608Q0 568 28 540 56 512 96 512L512 512 512 96Q512 56 540 28 568 0 608 0L800 0Q840 0 868 28 896 56 896 96L896 512 1312 512Q1352 512 1380 540 1408 568 1408 608Z" into mAddItemPath
// Make sure all the icons paths are 10 x 10, with centered at the origin
put rectangle [-5,-5,5,5] into mIconRect
setIconPath(mIconRect, mFoldedArrowPath)
setIconPath(mIconRect, mUnfoldedArrowPath)
setIconPath(mIconRect, mDeleteItemPath)
setIconPath(mIconRect, mAddItemPath)
variable tWidth as Real
put the width of mIconRect into mIconWidth
put 21 into mRowHeight
put true into mAlternateRowBackgrounds
put 6 into mMargin
put 15 into mIndentPixels
put "10,105,216" into mSelectedRowColor
put my height into mViewHeight
put my width into mViewWidth
put 0 into mHoverRow
put "" into mHoverIcon
put 0 into mViewTopPosition
put true into mFrameBorder
initialiseScrollbar()
put false into mReadOnly
put false into mArrayStyle
put true into mSortNumeric
put true into mSortAscending
put true into mRecalculate
put the empty array into mFoldState
put the empty array into mData
put convertArrayToList(mData, 0, the empty list) into mDataList
push the empty array onto front of mDataList
end handler
public handler OnSave(out rProperties as Array)
put the empty array into rProperties
put mData into rProperties["array"]
put mSelectedRowColor into rProperties["selected row color"]
put mReadOnly into rProperties["read only"]
put mArrayStyle into rProperties["array style"]
put mSortAscending into rProperties["sort ascending"]
put mSortNumeric into rProperties["sort numeric"]
return rProperties
end handler
public handler OnLoad(in pProperties as Array)
put pProperties["selected row color"] into mSelectedRowColor
put pProperties["read only"] into mReadOnly
if "array style" is among the keys of pProperties then
put pProperties["array style"] into mArrayStyle
end if
if "sort ascending" is among the keys of pProperties then
put pProperties["sort ascending"] into mSortAscending
end if
if "sort numeric" is among the keys of pProperties then
put pProperties["sort numeric"] into mSortNumeric
end if
setArrayData(pProperties["array"])
end handler
public handler OnPaint() returns nothing
// If anything has changed requiring a recalculation, update parameters
if mRecalculate then
updateParameters()
end if
variable tTop as Real
variable tPath as Path
put 0 into tTop
variable tDataItem as Array
put the empty array into tDataItem
variable tX as Integer
put 1 into tX
variable tTopOffset
put mViewTopPosition mod mRowHeight into tTopOffset
subtract tTopOffset from tTop
// Iterate from the first data item drawing each row until the can't display any more items
repeat with tX from mFirstDataItem up to the number of elements in mDataList
if tX is 1 then
paintFirstRow(tTop)
else
paintDataItem(element tX of mDataList, tTop, tX)
end if
if tTop > mViewHeight then
exit repeat
end if
add mRowHeight to tTop
end repeat
// Paint the scrollbar
paintScrollbar(this canvas, getPaint("scrollbar", "fill"))
// Draw the frame
if mFrameBorder is true then
put rectangle path of rectangle [0.5,0.5,mViewWidth-0.5,mViewHeight-0.5] into tPath
set the paint of this canvas to getPaint("frame","stroke")
stroke tPath on this canvas
end if
end handler
// Utility for painting the first (Add new element) row
private handler paintFirstRow(in pTop as Real)
// Apply any style to this data item
variable tStyle as String
put "" into tStyle
if mHoverRow is 1 then
put "_hover" after tStyle
end if
variable tPath as Path
put rectangle path of rectangle [0,pTop,mViewWidth,pTop+mRowHeight] into tPath
set the paint of this canvas to getPaint("row","fill" & tStyle)
fill tPath on this canvas
variable tLeft as Real
put mMargin into tLeft
set the paint of this canvas to getPaint("text","top row" & tStyle)
// Draw the plus icon
put mAddItemPath into tPath
translate tPath by [mMargin + mIconWidth / 2, pTop + mRowHeight / 2]
fill tPath on this canvas
// Draw text
fill text "Add new element" at left of rectangle [mIconWidth + 2 * mMargin, pTop, mViewWidth - 3 * mMargin - 2 * mIconWidth, pTop + mRowHeight] on this canvas
end handler
// Utility for painting a row with top pTop
private handler paintDataItem(in pDataItem as Array, in pTop as Real, in pRow as Integer)
// Apply any style to this data item
variable tStyle as String
put "" into tStyle
if pDataItem["selected"] is true then
put "_selected" after tStyle
end if
if pRow is mHoverRow then
put "_hover" after tStyle
end if
variable tPath as Path
// Draw the alternating row backgrounds if applicable
if mAlternateRowBackgrounds is true then
put rectangle path of rectangle [0,pTop,mViewWidth,pTop+mRowHeight] into tPath
if pRow mod 2 is 1 then
set the paint of this canvas to getPaint("row","fill" & tStyle)
else
set the paint of this canvas to getPaint("row","fill_alternate" & tStyle)
end if
fill tPath on this canvas
end if
variable tLeft as Real
put mMargin + (pDataItem["indent"] * mIndentPixels) into tLeft
// Draw the fold/unfold arrow if applicable
if not pDataItem["leaf"] then
if pDataItem["folded"] is true then
put mFoldedArrowPath into tPath
else
put mUnfoldedArrowPath into tPath
end if
translate tPath by [tLeft + mIconWidth / 2, pTop + mRowHeight / 2]
set the paint of this canvas to getPaint("text","fill" & tStyle)
fill tPath on this canvas
end if
add mIconWidth + mMargin to tLeft
// Draw the key
set the paint of this canvas to getPaint("text","disabled" & tStyle)
// AL-2015-07-26: [[ Bug 15752 ]] Only draw specified amount of key string
variable tKeyLength as Number
variable tKeyDisplay as String
put the number of chars in pDataItem["key"] into tKeyLength
if tKeyLength <= kMaxKeyDisplayChars then
put pDataItem["key"] into tKeyDisplay
else
put (char 1 to kMaxKeyDisplayChars of pDataItem["key"]) & "..." into tKeyDisplay
end if
if mArrayStyle then
put "[" & tKeyDisplay & "]" into tKeyDisplay
end if
fill text tKeyDisplay at left of rectangle [tLeft, pTop, mViewWidth - 3 * mMargin - 2 * mIconWidth, pTop + mRowHeight] on this canvas
variable tTextBounds as Rectangle
measure tKeyDisplay on this canvas
put the result into tTextBounds
put tTextBounds into pDataItem["keyRect"]
add the width of tTextBounds + mMargin to tLeft
// Draw the value if it is a 'leaf' (i.e. if it does not contain a sub-array)
if pDataItem["leaf"] then
// AL-2015-07-26: [[ Bug 15752 ]] Only draw specified amount of value string
variable tValueLength as Number
variable tValueDisplay as String
put the number of chars in pDataItem["string_value"] into tValueLength
if tValueLength <= kMaxValueDisplayChars then
put pDataItem["string_value"] into tValueDisplay
else
put (char 1 to kMaxValueDisplayChars of pDataItem["string_value"]) & "..." into tValueDisplay
end if
set the paint of this canvas to getPaint("text","fill" & tStyle)
fill text tValueDisplay at left of rectangle [tLeft, pTop, mViewWidth - 3 * mMargin - 2 * mIconWidth, pTop + mRowHeight] on this canvas
end if
// Draw the action icons
if mReadOnly is false and (pDataItem["selected"] is true or pRow is mHoverRow) then
put "fill" into tStyle
if pDataItem["selected"] then
put "_selected" after tStyle
end if
variable tRight as Real
put mViewWidth - scrollbarWidth() - mMargin into tRight
variable tThisIconStyle as String
variable tMouseX as Real
put the x of the mouse position into tMouseX
put tStyle into tThisIconStyle
put mDeleteItemPath into tPath
translate tPath by [tRight - mIconWidth / 2, pTop + mRowHeight / 2]
if pRow is mHoverRow and mHoverIcon is "delete" then
put "_hover" after tThisIconStyle
end if
set the paint of this canvas to getPaint("icon", tThisIconStyle)
fill tPath on this canvas
subtract mMargin + mIconWidth from tRight
put tStyle into tThisIconStyle
put mAddItemPath into tPath
translate tPath by [tRight - mIconWidth / 2, pTop + mRowHeight / 2]
if pRow is mHoverRow and mHoverIcon is "add" then
put "_hover" after tThisIconStyle
end if
set the paint of this canvas to getPaint("icon", tThisIconStyle)
fill tPath on this canvas
end if
end handler
public handler OnMouseDown() returns nothing
// Notify the scrollbar of a mouse down
checkScrollbarMouseDown()
end handler
public handler OnMouseMove() returns nothing
if scrollDragging() then
variable tScrollPositionRatio as Real
scrollbarDrag(mViewHeight)
put scrollRatio(mViewHeight) into tScrollPositionRatio
// Calculate the new top position of list
put (mDataHeight - mViewHeight) * tScrollPositionRatio into mViewTopPosition
ensureViewTopPosition()
updateFirstDataItem()
updateScrollbar(mViewWidth, mViewHeight, mDataHeight, mViewTopPosition)
redraw all
else
variable tRedraw as Boolean
put false into tRedraw
variable tNewHoverRow as Integer
put yPosToRowNumber(the y of the mouse position) into tNewHoverRow
if mHoverRow is not tNewHoverRow then
put tNewHoverRow into mHoverRow
put true into tRedraw
end if
if mHoverRow is 1 then
put "" into mHoverIcon
else
variable tHoverIcon as String
put xPosToIconString(the x of the mouse position) into tHoverIcon
if mHoverIcon is not tHoverIcon then
put tHoverIcon into mHoverIcon
put true into tRedraw
end if
end if
if tRedraw then
redraw all
end if
end if
end handler
public handler onMouseLeave() returns nothing
put 0 into mHoverRow
redraw all
end handler
public handler OnMouseUp() returns nothing
scrollbarMouseUp()
end handler
public handler OnMouseCancel() returns nothing
scrollbarMouseUp()
end handler
public handler OnMouseScroll(in pDeltaX as Real, in pDeltaY as Real) returns nothing
if mViewHeight < mDataHeight then
variable tOldTop as Real
put mViewTopPosition into tOldTop
if pDeltaY < 0 then
subtract mRowHeight from mViewTopPosition
else
add mRowHeight to mViewTopPosition
end if
ensureViewTopPosition()
// If nothing changed, don't update anything
if mViewTopPosition is tOldTop then
return
end if
updateFirstDataItem()
updateScrollbar(mViewWidth, mViewHeight, mDataHeight, mViewTopPosition)
redraw all
end if
end handler
public handler OnClick() returns nothing
// If the first row was clicked then add a new element
if mHoverRow is 1 then
addBaseLevelElement()
return
end if
// Click in the scrollbar region does nothing
if the x of the click position > mViewWidth - scrollbarWidth() then
return
end if
// Just return if the click was below all the data
if mHoverRow > the number of elements in mDataList then
return
end if
variable tData as Array
put the empty array into tData
put element mHoverRow of mDataList into tData
// Check if the delete icon was clicked
if mHoverIcon is "delete" then
removePath(tData["path"])
return
end if
// Check if the add icon was clicked
if mHoverIcon is "add" then
addUnderPath(tData["path"])
return
end if
// Check if the arrow was clicked
if not tData["leaf"] then
variable tLeft as Real
put tData["indent"] * mIndentPixels into tLeft
variable tX as Real
put the x of the click position into tX
if tX > tLeft and tX < tLeft + mIconWidth + 2 * mMargin then
if tData["folded"] is true then
unfoldPath(tData["path"])
else
foldPath(tData["path"])
end if
return
end if
end if
if the click count is 1 then
if tData["selected"] is true then
unselectPath(tData["path"])
else
selectPath(tData["path"])
end if
return
end if
if tData["leaf"] is not true then
if the click count > 1 then
if tData["folded"] is true then
unfoldPath(tData["path"])
else
foldPath(tData["path"])
end if
end if
end if
end handler
private handler yPosToRowNumber(in pYPos as Number) returns Integer
variable tRow as Number
put the floor of ((mViewTopPosition + pYPos) / mRowHeight + 1) into tRow
if mReadOnly then
add 1 to tRow
end if
return tRow
end handler
private handler xPosToIconString(in pXPos as Number) returns String
variable tLeft as Real
variable tRight as Real
put mViewWidth - scrollbarWidth() into tRight
if pXPos > tRight then
return ""
end if
put tRight - 2 * mMargin - 2 * mIconWidth into tLeft
if pXPos <= tLeft then
return ""
end if
if pXPos > tLeft and pXPos <= tLeft + mIconWidth then
return "add"
end if
return "delete"
end handler
public handler OnGeometryChanged()
// Only resizing necessitates a recalculation
if my height is not mViewHeight or my width is not mViewWidth then
put true into mRecalculate
end if
end handler
--------------------------------------------------------------------------------
--
-- Display calculations
--
--------------------------------------------------------------------------------
// Clamp mViewTopPosition
private handler ensureViewTopPosition()
variable tMinTop as Real
put 0 into tMinTop
// Make sure we don't try to scroll above 0
if mViewTopPosition < tMinTop then
put tMinTop into mViewTopPosition
// Make sure we don't try to scroll past the last displayable item
else if mDataHeight > mViewHeight and mViewTopPosition > mDataHeight - mViewHeight then
put mDataHeight - mViewHeight into mViewTopPosition
else if mDataHeight < mViewHeight then
put tMinTop into mViewTopPosition
end if
end handler
// Calculate mFirstDataItem from the view position
private handler updateFirstDataItem()
put the floor of (mViewTopPosition / mRowHeight) + 1 into mFirstDataItem
if mFirstDataItem < 1 then
put 1 into mFirstDataItem
end if
if mReadOnly then
add 1 to mFirstDataItem
end if
end handler
private handler updateParameters() returns nothing
put the number of elements in mDataList into mDataCount
if mReadOnly then
put mRowHeight * (mDataCount - 1) into mDataHeight
else
put mRowHeight * mDataCount into mDataHeight
end if
put my height into mViewHeight
put my width into mViewWidth
ensureViewTopPosition()
updateFirstDataItem()
// Calculate scrollbar dimensions
updateScrollbar(mViewWidth, mViewHeight, mDataHeight, mViewTopPosition)
put false into mRecalculate
end handler
--------------------------------------------------------------------------------
--
-- Utilities for widget display
--
--------------------------------------------------------------------------------
private handler getPaint(pLocation, pType) returns Paint
if pLocation is "background" then
if pType is "fill" then
return solid paint with stringToColor("178,178,178")
end if
else if pLocation is "icon" then
if pType is "fill" then
return solid paint with stringToColor("152,152,152")
else if pType is "fill_hover" then
return solid paint with stringToColor(mSelectedRowColor)
else if pType is "fill_selected" then
return solid paint with stringToColor("255,255,255")
else if pType is "fill_selected_hover" then
return solid paint with stringToColor("0,0,0,150")
end if
else if pLocation is "row" then
if pType is "fill" then
return solid paint with stringToColor("255,255,255")
else if pType is "fill_hover" then
return solid paint with stringToColor("235,235,235")
else if pType is "fill_selected" then
return solid paint with stringToColor(mSelectedRowColor)
else if pType is "fill_selected_hover" then
return solid paint with stringToColor(mSelectedRowColor & ",150")
else if pType is "fill_alternate" then
return solid paint with stringToColor("245,245,245")
else if pType is "fill_alternate_hover" then
return solid paint with stringToColor("230,230,230")
else if pType is "fill_alternate_selected" then
return solid paint with stringToColor(mSelectedRowColor)
else if pType is "fill_alternate_selected_hover" then
return solid paint with stringToColor(mSelectedRowColor & ",150")
end if
else if pLocation is "text" then
if pType is "disabled" then
return solid paint with stringToColor("0,0,0,150")
else if pType is "top row" then
return solid paint with stringToColor("152,152,152")
else if pType is "top row_hover" then
return solid paint with stringToColor("0,0,0,150")
else if pType is "disabled_hover" then
return solid paint with stringToColor("0,0,0,150")
else if pType is "disabled_selected" then
return solid paint with stringToColor("255,255,255,150")
else if pType is "disabled_selected_hover" then
return solid paint with stringToColor("255,255,255,150")
else if pType is "fill" then
return solid paint with stringToColor("0,0,0")
else if pType is "fill_hover" then
return solid paint with stringToColor("0,0,0")
else if pType is "fill_selected" then
return solid paint with stringToColor("255,255,255")
else if pType is "fill_selected_hover" then
return solid paint with stringToColor("255,255,255")
else if pType is "fill_alternate" then
return solid paint with stringToColor("0,0,0")
else if pType is "fill_alternate_hover" then
return solid paint with stringToColor("0,0,0")
else if pType is "fill_alternate_selected" then
return solid paint with stringToColor("255,255,255")
else if pType is "fill_alternate_selected_hover" then
return solid paint with stringToColor("255,255,255")
end if
else if pLocation is "scrollbar" then
if pType is "fill" then
return solid paint with stringToColor("0,0,0,50")
end if
else if pLocation is "frame" then
if pType is "stroke" then
return solid paint with stringToColor("188,188,188")
end if
end if
return solid paint with stringToColor("255,100,200")
end handler
private handler getFontName() returns String
return "Helvetica Neue"
return the name of the font of this canvas
end handler
private handler stringToColor(in pString as String) returns Color
variable tRed as Real
variable tGreen as Real
variable tBlue as Real
variable tAlpha as Real
variable tComponentList as List
split pString by "," into tComponentList
variable tComponentCount
put the number of elements in tComponentList into tComponentCount
if tComponentCount is not 3 and tComponentCount is not 4 then
// Invalid number of components detected
throw "Invalid color"
end if
put (element 1 of tComponentList) parsed as number into tRed
put (element 2 of tComponentList) parsed as number into tGreen
put (element 3 of tComponentList) parsed as number into tBlue
if tComponentCount is 4 then
put (element 4 of tComponentList) parsed as number into tAlpha
else
put 255 into tAlpha
end if
return color [ tRed/255, tGreen/255, tBlue/255, tAlpha/255 ]
end handler
private handler setIconPath(in pTargetRect as Rectangle, inout xPath as Path)
// Scale the icon
variable tBounds
put the bounding box of xPath into tBounds
// Scale appropriately
variable tXScale as Real
variable tYScale as Real
put the width of pTargetRect / the width of tBounds into tXScale
put the height of pTargetRect / the height of tBounds into tYScale
if tXScale > tYScale then
put tYScale into tXScale
else
put tXScale into tYScale
end if
scale xPath by [tXScale, tYScale]
variable tXTranslate as Real
variable tYTranslate as Real
put the bounding box of xPath into tBounds
put the left of pTargetRect - the left of tBounds into tXTranslate
put the top of pTargetRect - the top of tBounds into tYTranslate
variable tXDiff as Real
variable tYDiff as Real
put the width of pTargetRect - the width of tBounds into tXDiff
put the height of pTargetRect - the height of tBounds into tYDiff
// align center
divide tXDiff by 2
divide tYDiff by 2
translate xPath by [tXTranslate + tXDiff, tYTranslate + tYDiff]
end handler
--------------------------------------------------------------------------------
--
-- Handlers for data management and array/list conversions
--
--------------------------------------------------------------------------------
// Given a 'path' of array keys as a list, and a value, modify xArray such that
// the value of xArray[element 1 of pPath][element 2 of pPath]... is pValue
// creating the keys if necessary
private handler setValueOnPath(in pPath as List, in pValue as any, inout xArray as Array)
if the number of elements in pPath is 1 then
put pValue into xArray[element 1 of pPath]
else
if (element 1 of pPath) is not among the keys of xArray then
put the empty array into xArray[element 1 of pPath]
end if
setValueOnPath(element 2 to -1 of pPath, pValue, xArray[element 1 of pPath])
end if
end handler
// Return the whole stored array
private handler getArrayData() returns Array
return mData
end handler
-- Sorts numerically, converting strings to numbers where possible
private handler CompareKeysNumeric(in pLeft as any, in pRight as any) as Integer
variable tLeft as optional Number
variable tRight as optional Number
if pLeft is a number then
put pLeft into tLeft
else
put pLeft parsed as number into tLeft
end if
if pRight is a number then
put pRight into tRight
else
put pRight parsed as number into tRight
end if
if tRight is tLeft then
return 0
end if
if tRight is nothing then
return -1
end if
if tLeft is nothing then
return 1
end if
if tLeft < tRight then
if mSortAscending then
return -1
else
return 1
end if
end if
if mSortAscending then
return 1
else
return -1