forked from livecode/livecode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsegmented.lcb
More file actions
1348 lines (1073 loc) · 41.9 KB
/
Copy pathsegmented.lcb
File metadata and controls
1348 lines (1073 loc) · 41.9 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 - 2016 LiveCode 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 that shows a row of selectable horizontal segments.
A segmented control is a horizontal control that is made up of
multiple segments, where each segment functions as a discrete button.
Each segment can either show a <itemLabels|label> or an
<itemIcons|icon>.
One or more of the segments can be <hilitedItems|highlighted> by
clicking on them. By default, only one segment at a time can be
highlighted, but it is possible to
<multipleHilites|allow multiple segments to be highlighted>.
The segmented control is great for:
- displaying a set of different options in a settings window (for
example, it is used to display text alignment in the LiveCode
property inspector)
- switching between different cards in a stack
- displaying a set of toggleable settings
References: itemLabels (property), itemIcons (property),
hilitedItems (property), multipleHilites (property)
Name: hiliteChanged
Type: message
Syntax: hiliteChanged
Summary: Sent when the hilite of the segmented control widget changes
Description:
Handle the <hiliteChanged> message in order to respond to a change in the hilited items
of the segmented control.
Name: foreColor
Type: property
Syntax: set the foreColor of <widget> to <color>
Syntax: get the foreColor of <widget>
Summary: The default label or icon color
Description:
The <foreColor> property controls the color used to draw segments'
labels or icons when they are not highlighted.
Related: hilitedTextColor (property)
Name: backColor
Type: property
Syntax: set the backColor of <widget> to <color>
Syntax: get the backColor of <widget>
Summary: The default background color
Description:
The <backColor> property controls the background color of segments
when they are not highlighted.
Related: hiliteColor (property)
Name: hiliteColor
Type: property
Syntax: set the hiliteColor of <widget> to <color>
Syntax: get the hiliteColor of <widget>
Summary: The background color of highlighted segments
Description:
The <hiliteColor> property controls the background color
of the segments that are currently highlighted.
Related: hilitedItems (property), hilitedItemNames (property),
backColor (property)
Name: borderColor
Type: property
Syntax: set the borderColor of <widget> to <color>
Syntax: get the borderColor of <widget>
Summary: The border color
Description:
The <borderColor> property controls the color used to draw the
segmented control's background and the color of the dividers between
the segments.
Related: showBorder (property)
**/
widget com.livecode.widget.segmented
-- dependency declarations
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.iconsvg
use com.livecode.library.widgetutils
use com.livecode.library.scriptitems
-- metadata
metadata title is "Segmented Control"
metadata author is "LiveCode"
metadata version is "1.2.0"
metadata svgicon is "M72.6,0H7.7C3.4,0,0,3.4,0,7.7v14c0,4.2,3.4,7.7,7.7,7.7h64.9c4.2,0,7.7-3.4,7.7-7.7v-14C80.2,3.4,76.8,0,72.6,0z M7.8,13.5h8v2.3h-8V13.5z M19.7,20.6h-12v-2.3h12V20.6z M19.7,11h-12V8.8h12V11z M27.8,25.5h-1V3.8h1V25.5z M46.1,20.6h-12v-2.3h12V20.6zM36.1,15.7v-2.3h8v2.3H36.1z M46.1,11h-12V8.8h12V11z M54.4,25.5h-1V3.8h1V25.5z M72.7,20.6h-12v-2.3h12V20.6z M72.7,15.7h-8v-2.3h8V15.7z M72.7,11h-12V8.8h12V11z"
metadata preferredSize is "200,32"
metadata _ide is "true"
-- property declarations
metadata foregroundColor.editor is "com.livecode.pi.color"
metadata foregroundColor.default is "0,0,0"
metadata foregroundColor.section is "Colors"
metadata foregroundColor.label is "Segment Label Color"
metadata backgroundColor.editor is "com.livecode.pi.color"
metadata backgroundColor.default is "244,244,244"
metadata backgroundColor.section is "Colors"
metadata backgroundColor.label is "Segment color"
metadata hiliteColor.editor is "com.livecode.pi.color"
metadata hiliteColor.default is "10,95,244"
metadata hiliteColor.section is "Colors"
metadata hiliteColor.label is "Selected segment color"
metadata borderColor.editor is "com.livecode.pi.color"
metadata borderColor.default is "109,109,109"
metadata borderColor.section is "Colors"
metadata borderColor.label is "Border color"
/**
Syntax: set the hilitedTextColor of <widget> to <pColor>
Syntax: get the hilitedTextColor of <widget>
Summary: The label or icon color for highlighted segments
Description:
The <hilitedTextColor> property controls the color used to draw
segments' labels or icons when they are highlighted.
Related: foreColor (property)
**/
property hilitedTextColor get getSelectedLabelColor set setSelectedLabelColor
metadata hilitedTextColor.editor is "com.livecode.pi.color"
metadata hilitedTextColor.default is "255,255,255"
metadata hilitedTextColor.section is "Colors"
metadata hilitedTextColor.label is "Hilited segment label color"
metadata themeClass.default is "list"
metadata themeClass.user_visible is "false"
/**
Syntax: set the horizontal of <widget> to {true | false}
Syntax: get the horizontal of <widget>
Summary: Whether the segmented widget is horizontal or not
Description:
Use the <horizontal> property to control whether the segmented widget
is laid out horizontally or vertically.
**/
property horizontal get mIsHorizontal set SetHorizontal
metadata horizontal.editor is "com.livecode.pi.boolean"
metadata horizontal.default is "true"
metadata horizontal.label is "Horizontal"
-- private instance variables
private variable mMultiSelect as Boolean
private variable mToggleHilites as Boolean
private variable mSegmentNames as List
private variable mSegmentLabels as List
private variable mSegmentIcons as List
private variable mSelectedIcons as List
private variable mSegmentDisplay as String
private variable mSegmentMinExtent as List
private variable mSegmentSelected as List
private variable mShowFrameBorder as Boolean
private variable mGeometryIsChanged as Boolean
private variable mNumSegments as Integer
private variable mPerimeter as Path
private variable mLines as List -- list of line paths
private variable mRadius as Real
private variable mCalculatedExtents as List
private variable mSelectedLabelColor as optional Color
private variable mIsHorizontal as Boolean
-- constants
constant kIconSize is 16
constant kTextSize is 13
constant kIconPaddingRatio is 0.2
constant kDefaultSegmentName is "segment"
constant kDefaultSegmentIcon is "circle"
constant kDefaultSegmentLabel is "Label"
constant kDefaultSegmentMinExtent is 50
constant kDefaultSelectedLabelColor is [1,1,1]
constant kPostMessage is true
constant kDontPostMessage is false
--
public handler OnSave(out rProperties as Array)
put the empty array into rProperties
put mMultiSelect into rProperties["multiSelect"]
put mToggleHilites into rProperties["toggleHilites"]
put mSegmentNames into rProperties["segmentNames"]
put mSegmentLabels into rProperties["segmentLabels"]
put mSegmentIcons into rProperties["segmentIcons"]
put mSelectedIcons into rProperties["selectedIcons"]
put mSegmentDisplay into rProperties["segmentDisplay"]
put mSegmentMinExtent into rProperties["segmentMinExtent"]
put mSegmentMinExtent into rProperties["segmentMinWidth"]
put getSelected() into rProperties["selectedSegment"]
put mShowFrameBorder into rProperties["showFrameBorder"]
put mIsHorizontal into rProperties["horizontal"]
put mRadius into rProperties["cornerRadius"]
if mSelectedLabelColor is not nothing then
put colorToString(mSelectedLabelColor, false) into rProperties["selectedLabelColor"]
end if
end handler
public handler OnLoad(in pProperties as Array)
put pProperties["segmentNames"] into mSegmentNames
put the number of elements in mSegmentNames into mNumSegments
put pProperties["multiSelect"] into mMultiSelect
if "toggleHilites" is among the keys of pProperties then
put pProperties["toggleHilites"] into mToggleHilites
end if
put pProperties["segmentLabels"] into mSegmentLabels
put pProperties["segmentIcons"] into mSegmentIcons
put pProperties["selectedIcons"] into mSelectedIcons
put pProperties["segmentDisplay"] into mSegmentDisplay
if "segmentMinWidth" is among the keys of pProperties then
put pProperties["segmentMinWidth"] into mSegmentMinExtent
else if "segmentMinExtent" is among the keys of pProperties then
put pProperties["segmentMinExtent"] into mSegmentMinExtent
end if
put pProperties["showFrameBorder"] into mShowFrameBorder
setSelected(pProperties["selectedSegment"], kDontPostMessage)
if "cornerRadius" is among the keys of pProperties then
put pProperties["cornerRadius"] into mRadius
end if
if "selectedLabelColor" is among the keys of pProperties then
put stringToColor(pProperties["selectedLabelColor"]) into mSelectedLabelColor
end if
if "horizontal" is among the keys of pProperties then
put pProperties["horizontal"] into mIsHorizontal
end if
end handler
public handler OnCreate()
// Set the default values of all the properties
put false into mMultiSelect
put true into mToggleHilites
put ["segment1","segment2","segment3"] into mSegmentNames
put ["Label 1","Label 2","Label 3"] into mSegmentLabels
put ["align left","align center","align right"] into mSegmentIcons
put ["align left","align center","align right"] into mSelectedIcons
put [false, false, false] into mSegmentSelected
put "text" into mSegmentDisplay
put [kDefaultSegmentMinExtent, kDefaultSegmentMinExtent, kDefaultSegmentMinExtent] into mSegmentMinExtent
put true into mShowFrameBorder
put true into mIsHorizontal
// Initialise other variables
put true into mGeometryIsChanged
put (the number of elements in mSegmentNames) into mNumSegments
put the empty path into mPerimeter
put the empty list into mLines
put 15 into mRadius
put [] into mCalculatedExtents
end handler
public handler OnPaint()
if mGeometryIsChanged then
calculateExtents()
-- update mPerimeter and mLines variables if the geometry has changed
updatePerimeter()
updateLines()
end if
drawSegments()
if mShowFrameBorder then
-- draw the lines to separate the segments
set the antialias of this canvas to false
set the stroke width of this canvas to 1
set the paint of this canvas to my border paint
variable tLine
repeat for each element tLine in mLines
stroke tLine on this canvas
end repeat
set the antialias of this canvas to true
--
-- draw the perimeter of the control
set the paint of this canvas to my border paint
set the stroke width of this canvas to 1
stroke mPerimeter on this canvas
end if
--
put false into mGeometryIsChanged
end handler
public handler OnGeometryChanged()
put true into mGeometryIsChanged
end handler
private handler clickPosToComponent(in pLoc as Point) returns Integer
variable tSegmentIndex as Integer
repeat with tSegmentIndex from 1 up to mNumSegments
if pLoc is within fetchBounds(tSegmentIndex) then
return tSegmentIndex
end if
end repeat
return 0
end handler
public handler OnClick() returns nothing
variable tLabel as String
variable tSegment
variable tCount as Integer
variable tSegmentIndex as Integer
put clickPosToComponent(the click position) into tSegmentIndex
if tSegmentIndex is 0 then
return
end if
if mMultiSelect is false then
-- If cannot multiselect, then need to select the clicked-on segment and
-- deselect the currently selected segment. If the clicked-on segment is
-- already selected, then act according to toggleHilites property
if mSegmentSelected[tSegmentIndex] and mToggleHilites then
setSelected([], kPostMessage)
else if not mSegmentSelected[tSegmentIndex] then
setSelected([tSegmentIndex], kPostMessage)
end if
else
-- If can multiselect, then select the clicked-on segment if it is
-- unselected. If it is selected, act according to toggleHilites property
variable tState as List
if mSegmentSelected[tSegmentIndex] and mToggleHilites then
put false into mSegmentSelected[tSegmentIndex]
post "hiliteChanged"
redraw all
else if not mSegmentSelected[tSegmentIndex] then
put true into mSegmentSelected[tSegmentIndex]
post "hiliteChanged"
redraw all
end if
end if
end handler
-- Pixel hinting support. These handlers round a horizontal or
-- vertical pixel span to a whole number of pixels, in such a way that
-- the input span is always fully contained within the output span.
handler hintLower(in pBound as Number) returns Number
return the ceiling of (pBound - 0.5)
end handler
handler hintUpper(in pBound as Number) returns Number
return the floor of (pBound + 0.5)
end handler
private handler drawSegments() returns nothing
variable tSegmentIndex as Integer
variable tLabel as String
variable tStart as Real
variable tIsIn as Boolean
variable tBorderOffset as Real
put 0 into tStart
set the font of this canvas to my font
if mShowFrameBorder then
put 0.5 into tBorderOffset // border is hard coded at 1 pixel
else
put 0 into tBorderOffset
end if
repeat with tSegmentIndex from 1 up to mNumSegments
variable tExtent as Real
put fetchExtent(tSegmentIndex) into tExtent
put mSegmentSelected[tSegmentIndex] into tIsIn
if tIsIn then
set the paint of this canvas to my highlight paint
else
set the paint of this canvas to my background paint
end if
variable tSegmentRect as List
if mIsHorizontal then
put [hintLower(tStart), tBorderOffset, \
hintUpper(tStart + tExtent), my height-tBorderOffset] \
into tSegmentRect
else
put [tBorderOffset, hintLower(tStart), \
my width - tBorderOffset, hintUpper(tStart + tExtent)] \
into tSegmentRect
end if
variable tStartIndex as Integer
variable tEndIndex as Integer
if mIsHorizontal then
put 1 into tStartIndex
put 3 into tEndIndex
else
put 2 into tStartIndex
put 4 into tEndIndex
end if
if tSegmentIndex is 1 then
put tBorderOffset into element tStartIndex of tSegmentRect
end if
if tSegmentIndex is mNumSegments then
subtract tBorderOffset from element tEndIndex of tSegmentRect
end if
save state of this canvas
clip to rectangle tSegmentRect on this canvas
-- For the rounded rectangle segments at the ends, we fill the round rect beyond the
-- curvature and use the clip rect to display as we would like.
if tSegmentIndex = 1 and tSegmentIndex = mNumSegments then
fill rounded rectangle path of rectangle tSegmentRect with radius mRadius on this canvas
else if tSegmentIndex = 1 then
put tStart + tExtent + mRadius into element tEndIndex of tSegmentRect
fill rounded rectangle path of rectangle tSegmentRect with radius mRadius on this canvas
else if tSegmentIndex = mNumSegments then
put tStart - mRadius into element tStartIndex of tSegmentRect
fill rounded rectangle path of rectangle tSegmentRect with radius mRadius on this canvas
else
fill rectangle path of rectangle tSegmentRect on this canvas
end if
restore state of this canvas
if tIsIn then
if mSelectedLabelColor is nothing then
-- set the paint of the label of the selected segment(s)
set the paint of this canvas to solid paint with color kDefaultSelectedLabelColor
else
set the paint of this canvas to solid paint with mSelectedLabelColor
end if
else
set the paint of this canvas to my foreground paint
end if
variable tIconPath as Path
variable tLabelRect as Rectangle
variable tBound as Real
if mIsHorizontal then
put my height into tBound
else
put my width into tBound
end if
if mSegmentDisplay is "icons" then
-- Compute the minimum margin required to provide the correct
-- padding ratio.
variable tIconMargin as Number
if tExtent > tBound then
put tBound * kIconPaddingRatio into tIconMargin
else
put tExtent * kIconPaddingRatio into tIconMargin
end if
-- Compute the corresponding icon bounding box
if mIsHorizontal then
put rectangle [tStart + tIconMargin, \
tIconMargin, \
tStart + tExtent - tIconMargin, \
my height - tIconMargin] into tLabelRect
else
put rectangle [tIconMargin, \
tStart + tIconMargin, \
my width - tIconMargin, \
tStart + tExtent - tIconMargin] into tLabelRect
end if
if tIsIn then
put path iconSVGPathFromName(element tSegmentIndex of mSelectedIcons) into tIconPath
else
put path iconSVGPathFromName(element tSegmentIndex of mSegmentIcons) into tIconPath
end if
constrainPathToRect(tLabelRect, tIconPath)
fill tIconPath on this canvas
else
if mIsHorizontal then
put rectangle [tStart, 0, tStart + tExtent, my height] into tLabelRect
else
put rectangle [0, tStart, my width, tStart + tExtent] into tLabelRect
end if
put (element tSegmentIndex of mSegmentLabels) into tLabel
fill text tLabel at center of tLabelRect on this canvas
end if
add tExtent to tStart
end repeat
end handler
private handler updateLines() returns nothing
variable tSegmentIndex as Integer
variable tExtent as Real
variable tStart as Real
variable tBorderOffset as Real
if mShowFrameBorder then
put 0.5 into tBorderOffset // border is hard coded at 1 pixel
else
put 0 into tBorderOffset
end if
put the empty list into mLines
put 0 into tStart
repeat with tSegmentIndex from 1 up to mNumSegments
put fetchExtent(tSegmentIndex) into tExtent
add tExtent to tStart
if tSegmentIndex < mNumSegments then
if mIsHorizontal then
push line path from point [tStart, tBorderOffset] to point [tStart, my height-tBorderOffset] onto back of mLines
else
push line path from point [tBorderOffset, tStart] to point [my width-tBorderOffset, tStart] onto back of mLines
end if
end if
end repeat
end handler
private handler updatePerimeter() returns nothing
variable tRect as Rectangle
put rectangle [0.5, 0.5, (the trunc of my width) - 0.5, (the trunc of my height) - 0.5] into tRect
put rounded rectangle path of tRect with radius mRadius into mPerimeter
end handler
private handler fetchExtent(in pSegment as Integer) returns Real
if pSegment is 0 then
return 0
end if
return mCalculatedExtents[pSegment]
end handler
private handler calculateExtents() returns nothing
variable tBound as Real
if mIsHorizontal then
put my width into tBound
else
put my height into tBound
end if
variable tCount as Number
// Retain existing behavior if display is "icons"
if mSegmentDisplay is "icons" then
variable tExtent as Real
put tBound / mNumSegments into tExtent
variable tExtents as List
put [] into tExtents
repeat with tCount from 1 up to mNumSegments
push the maximum of tExtent and mSegmentMinExtent[tCount] onto tExtents
end repeat
put tExtents into mCalculatedExtents
return
end if
// We want to be able to fit as much of the text in as possible whilst
// respecting the minExtents of each segment.
// Measure the labels
variable tTextSizes as List
put [] into tTextSizes
variable tTotal as Number
put 0 into tTotal
variable tTextBounds as Rectangle
repeat with tCount from 1 up to mNumSegments
measure mSegmentLabels[tCount] on this canvas into tTextBounds
if mIsHorizontal then
push the width of tTextBounds onto back of tTextSizes
else
push the height of tTextBounds onto back of tTextSizes
end if
add the tail of tTextSizes to tTotal
end repeat
// Work out how much to add to respect min extents
variable tOK as Boolean
put true into tOK
variable tDifference as Number
repeat with tCount from 1 up to mNumSegments
put mSegmentMinExtent[tCount] - tTextSizes[tCount] into tDifference
if tDifference > 0 then
put mSegmentMinExtent[tCount] into tTextSizes[tCount]
add tDifference to tTotal
end if
end repeat
// If there is still space to expand, do so
if tTotal < tBound then
variable tNeededIncrease as Number
put (tBound - tTotal) / mNumSegments into tNeededIncrease
repeat with tCount from 1 up to mNumSegments
add tNeededIncrease to tTextSizes[tCount]
end repeat
end if
put tTextSizes into mCalculatedExtents
end handler
private handler fetchBounds(in pSegment as Integer) returns Rectangle
variable tStart as Real
put 0 into tStart
variable tSegmentIndex as Integer
repeat with tSegmentIndex from 1 up to pSegment-1
add fetchExtent(tSegmentIndex) to tStart
end repeat
variable tExtent as Real
put fetchExtent(pSegment) into tExtent
if mIsHorizontal then
return rectangle [tStart, 0, tStart + tExtent, my height]
else
return rectangle [0, tStart, my width, tStart + tExtent]
end if
end handler
private handler getSelectedLabelColor() returns String
if mSelectedLabelColor is nothing then
return ""
end if
return colorToString(mSelectedLabelColor, false)
end handler
private handler setSelectedLabelColor(in pColor as String)
if pColor is "" then
put nothing into mSelectedLabelColor
else
put stringToColor(pColor) into mSelectedLabelColor
end if
redraw all
end handler
handler setSelected(in pIndices as List, in pPostMessage as Boolean) returns nothing
variable tState as List
variable tIndex as Number
-- Generate a new blank selection state
repeat with tIndex from 1 up to mNumSegments
push false onto tState
end repeat
-- Select any valid requested segments, or the first valid
-- requested segment if multi-selection is disabled
repeat for each element tIndex in pIndices
if tIndex > mNumSegments then
next repeat
end if
put true into tState[tIndex]
if not mMultiSelect then
exit repeat
end if
end repeat
if tState is mSegmentSelected then
return -- No change
end if
put tState into mSegmentSelected
redraw all
if pPostMessage then
post "hiliteChanged"
end if
end handler
handler getSelected() returns List
variable tIndices as List
variable tIndex as Number
variable tSelected as Boolean
repeat for each element tSelected in mSegmentSelected
add 1 to tIndex
if tSelected then
push tIndex onto tIndices
end if
end repeat
return tIndices
end handler
handler updateSelected()
setSelected(getSelected(), kPostMessage)
end handler
----------------------------------------------------------------
-- Property management
----------------------------------------------------------------
-- Helper function for updating a property that contains a list of
-- strings from a LiveCode Script "item" list. It ensures that the
-- xListProperty variable always contains the same number of elements
-- as the widget has segments.
--
-- pStringValue: comma-delimited string provided from LCS
-- pDefaultValue: value to use for extra elements if pStringValue
-- doesn't contain enough items
-- xListProperty: widget state variable to be updated
handler updateStringListProperty(in pStringValue as String, \
in pDefaultValue as optional any, out xListProperty as List) \
returns nothing
variable tEntries as List
put parseItemsAsStringList(pStringValue, mNumSegments, pDefaultValue) \
into tEntries
if tEntries is xListProperty then
return -- No changes
end if
put tEntries into xListProperty
put true into mGeometryIsChanged
redraw all
end handler
-- Helper function for turning an "item" string containing numbers
-- into a per-segment list of property values. If there are any
-- non-empty items that can't be parsed as a number, an error is
-- thrown. Any empty items encountered are replaced with pDefault,
-- and the returned list is padded out to mNumSegments elements with
-- pDefault. Extra elements beyond mNumSegments are discarded.
handler parseNumberListProperty(in pItems as String, \
in pDefault as optional Number) returns List
return parseItemsAsNumberList(pItems, mNumSegments, pDefault)
end handler
/**
Syntax: set the itemCount of <widget> to <number>
Syntax: get the itemCount of <widget>
Summary: The number of segments shown
Description:
The <itemCount> property can be used to obtain or to set the number of
segments shown by the segmented control.
When you set the <itemCount> to a number larger than the current
number of segments in the control, new segments are added to the end
of the control with unique <itemNames|names> and <itemLabels|labels>.
When you set the <itemCount> to a number smaller than the current
number of segments in the control, segments are discarded from the end
of the control. If this results in discarding a segment that's
currently highlighted, the <hiliteChanged> message may be sent.
The segmented control must always have at least one segment.
Related: itemNames (property), itemLabels (property), hiliteChanged (message)
**/
property itemCount get mNumSegments set setSegmentCount
metadata itemCount.editor is "com.livecode.pi.integer"
metadata itemCount.step is "1"
metadata itemCount.min is "1"
metadata itemCount.label is "Number of segments"
handler setSegmentCount(in pCount as Number)
if pCount < 1 or the floor of pCount is not pCount then
throw "the itemCount must be a positive integer"
end if
if pCount < mNumSegments then
reduceSegmentCount(pCount)
else if pCount > mNumSegments then
increaseSegmentCount(pCount)
else
return -- No change
end if
put true into mGeometryIsChanged
redraw all
end handler
handler reduceSegmentCount(in pCount as Number)
-- Truncate
delete element (pCount + 1) to -1 of mSegmentNames
delete element (pCount + 1) to -1 of mSegmentLabels
delete element (pCount + 1) to -1 of mSegmentIcons
delete element (pCount + 1) to -1 of mSelectedIcons
delete element (pCount + 1) to -1 of mSegmentMinExtent
put pCount into mNumSegments
-- This may have caused the highlighting to change by deleting the
-- currently-highlighted segment.
updateSelected()
end handler
handler increaseSegmentCount(in pCount as Number)
-- Generate new segments. They need to have unique
-- names and labels, and default icons.
variable tUniqueNum as Number
put mNumSegments into tUniqueNum
variable tSegmentNum
repeat with tSegmentNum from (mNumSegments + 1) up to pCount
-- Find a suitable unique name and label for this segment
variable tName as String
variable tLabel as String
repeat forever
add 1 to tUniqueNum
put kDefaultSegmentName & intToString(tUniqueNum) into tName
if not (tName is in mSegmentNames) then
exit repeat
end if
end repeat
put kDefaultSegmentLabel && intToString(tUniqueNum) into tLabel
push tName onto mSegmentNames
push tLabel onto mSegmentLabels
push kDefaultSegmentIcon onto mSegmentIcons
push kDefaultSegmentIcon onto mSelectedIcons
push kDefaultSegmentMinExtent onto mSegmentMinExtent
end repeat
put pCount into mNumSegments
updateSelected()
end handler
/**
Syntax: set the itemNames of <widget> to <nameList>
Syntax: get the itemNames of <widget>
Summary: The names used to identify segments
Value(string): A comma-delimited list of names for the segments.
Description:
The names of each segment in the control. The <itemNames> can be a
more convenient way to identify the segments than by their positions.
You are recommended to use a non-empty, unique name for each segment.
When you set the <itemNames> to a string that has fewer names than
the <itemCount>, the remaining segments' names are set to empty.
When you set the <itemNames> to a string that that has more names than
the <itemCount>, the extra names are ignored.
Related: itemCount (property>, itemLabels (property),
hilitedItemNames (Property)
**/
property itemNames get getSegmentNames set setSegmentNames
metadata itemNames.editor is "com.livecode.pi.editorList"
metadata itemNames.subeditor is "com.livecode.pi.string"
metadata itemNames.delimiter is ","
metadata itemNames.default is "segment1,segment2,segment3"
metadata itemNames.label is "Segment names"
handler setSegmentNames(in pNames as String) returns nothing
updateStringListProperty(pNames, "", mSegmentNames)
end handler
private handler getSegmentNames() returns String
return formatStringListAsItems(mSegmentNames)
end handler
/**
Syntax: set the itemLabels of <widget> to <labelList>
Syntax: get the itemLabels of <widget>
Summary: The labels displayed by each segment
Value(string): A comma-delimited list of labels for the segments.
Description:
The labels of each segment in the control. The <itemLabels> are
displayed by the widget when the <itemStyle> is set to show labels.
If any of the <itemLabels> are empty, those segments have no label.
When you set the <itemLabels> to a string that has fewer labels than
the <itemCount>, the remaining segments' labels are set to empty.
When you set the <itemLabels> to a string that has more labels than
the <itemCount>, the extra labels are ignored.
Related: itemCount (property), itemStyle (property),
foreColor (property)
**/
property itemLabels get getSegmentLabels set setSegmentLabels
metadata itemLabels.editor is "com.livecode.pi.editorList"
metadata itemLabels.subeditor is "com.livecode.pi.string"
metadata itemLabels.delimiter is ","
metadata itemLabels.default is "Label 1,Label 2,Label 3"
metadata itemLabels.label is "Segment labels"
handler setSegmentLabels(in pLabels as String) returns nothing
updateStringListProperty(pLabels, "", mSegmentLabels)
end handler
handler getSegmentLabels() returns String
return formatStringListAsItems(mSegmentLabels)
end handler
/**
Syntax: set the itemIcons of <widget> to <iconNames>
Syntax: get the itemIcons of <widget>
Summary: The icons displayed by each segment
Value(string): A comma-delimited list of icon names for the segments
Description:
The icons displayed for each segment in the control when not
highlighted. The <itemIcons> are displayed by the widget when the
<itemStyle> is set to show icons.
The <itemIcons> must each be one of the predefined graphics provided
by the "IconSVG" library. You can get a list of available predefined
path names by running `put iconNames()` in the Message Box. If any of
the <itemIcons> are empty, those segments have no icon when not
hilited.
When you set the <itemIcons> to a string that has fewer icon names
than the <itemCount>, the remaining segments' icons are set to empty
(no icon).
When you set the <itemIcons> to a string that has more icon names than
the <itemCount>, the extra icons are ignored.
Related: itemCount (property), itemStyle (property),
itemHilitedIcons (property), foreColor (property),
hilitedItems (property)
**/
property itemIcons get getSegmentIcons set setSegmentIcons
metadata itemIcons.label is "Segment Icons"
metadata itemIcons.editor is "com.livecode.pi.editorlist"
metadata itemIcons.subeditor is "com.livecode.pi.svgicon"
metadata itemIcons.delimiter is ","
metadata itemIcons.default is "align left,align center,align right"
metadata itemIcons.section is "Icons"
handler setSegmentIcons(in pIconNames as String) returns nothing
updateStringListProperty(pIconNames, "", mSegmentIcons)
end handler
handler getSegmentIcons() returns String
return formatStringListAsItems(mSegmentIcons)