This repository was archived by the owner on Aug 31, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 226
Expand file tree
/
Copy pathswitchbutton.lcb
More file actions
581 lines (459 loc) · 15.2 KB
/
switchbutton.lcb
File metadata and controls
581 lines (459 loc) · 15.2 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
/*
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/>. */
/**
This widget is a switch button, consisting of two mutually exclusive choices or states.
Name: hiliteChanged
Type: message
Syntax: hiliteChanged
Summary: Sent when the switch is changed to either the on or off position
Example:
on hiliteChanged
set the visible of group 1 to the highlight of me
end hiliteChanged
Description:
Handle the hiliteChanged message in the widget's object script to respond to
the user switching the button on or off.
Name: backColor
Type: property
Syntax: set the backColor of <widget> to <pColor>
Syntax: get the backColor of <widget>
Summary: Controls the background color of the switch button
Description:
Use the <backColor> property to control the off-position fill color of the
switch button.
Name: hiliteColor
Type: property
Syntax: set the hiliteColor of <widget> to <pColor>
Syntax: get the hiliteColor of <widget>
Summary: Controls the color of the switch button when it is in the on position
Description:
Use the <hiliteColor> property to control the on-position fill color of the
switch button.
Name: borderColor
Type: property
Syntax: set the borderColor of <widget> to <pColor>
Syntax: get the borderColor of <widget>
Summary: Controls the color of the switch button when it is in the on position
Description:
Use the <borderColor> property to control the on-position fill color of the
switch button.
*/
-- declaring extension as widget, followed by identifier
widget com.livecode.widget.switchbutton
--
-- dependancy declarations
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.iconsvg
use com.livecode.library.widgetutils
--
-- adding metadata to ensure the extension displays correctly in livecode
metadata title is "Switch Button"
metadata author is "LiveCode"
metadata version is "2.0.0"
metadata preferredSize is "64,48"
metadata svgicon is "M47.3,0H18.5C8.3,0,0,8.3,0,18.5v0C0,28.7,8.3,37,18.5,37h28.8c10.2,0,18.5-8.3,18.5-18.5v0C65.8,8.3,57.5,0,47.3,0zM19.8,33.5c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C34.8,26.8,28,33.5,19.8,33.5z"
--
metadata backgroundColor.editor is "com.livecode.pi.color"
metadata backgroundColor.default is "244,244,244"
metadata backgroundcolor.section is "Colors"
metadata backgroundColor.label is "Background 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 "Hilite 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 theme of <widget> to <pWidgetTheme>
Syntax: get the theme of <widget>
Summary: Specifies the theme to use when drawing the switch button.
Value (string):
The <theme> of the switch button is a name identifying the
style to use when drawing it.
Description:
Use the <theme> property to control the general appearance of the switch
button. The currently-supported values are "native", "iOS" and "Android".
**Note**: The value of the <theme> property is not saved by the switch button.
Set the <theme> property to preview the way the switch button will appear when
used on an Android or iOS device.
*/
property "theme" get mTheme set setWidgetTheme
metadata theme.editor is "com.livecode.pi.enum"
metadata theme.options is "native,iOS,Android"
metadata theme.default is "native"
metadata theme.label is "Theme"
/**
Syntax: set the highlight of <widget> to {true | false}
Syntax: get the highlight of <widget>
Summary: Whether the switch is on or off
Value (boolean): `true` if the switch is in the on position; `false` otherwise.
Description:
Use this property to determine whether the switch button displays as on.
*/
property "highlight" get mSwitchIsOn set setSwitch
metadata highlight.default is "false"
metadata highlight.label is "Hilited"
/**
Syntax: set the showBorder of <widget> to {true|false}
Syntax: get the showBorder of <widget>
Summary: Whether the widget has a border or not.
Description:
Use the <showBorder> property to control whether the switch button has a border
around it or not
*/
property showBorder get mShowFrameBorder set setShowFrameBorder
metadata showBorder.default is "true"
-- private instance variables
private variable mTheme as String
private variable mWidgetTheme as String
private variable mSwitchIsOn as Boolean
private variable mShowFrameBorder as Boolean
private variable mIsPressed as Boolean
private variable mXClick as Real
private variable mMouseHasMoved as Boolean
private variable mSwitchIsInOnPosition as Boolean
private variable mClickableRect as Rectangle
constant kGeometry is { \
"margin-px": { "iOS": 4, "android": 4 }, \
"length": { "iOS": 1.75, "android": 2 }, \
"trackwidth": { "iOS": 1, "android": 0.66 }, \
"thumbstretch": { "iOS": 0.5, "android": 0 } \
}
constant kPaints is { \
"track": { \
"off": { \
"fill": { \
"iOS": [["background", 1]], "android": [["background", 1], ["border", 0.5]] \
}, \
"stroke": { \
"iOS": [["border", 1]], "android": [["border", 1]] \
} \
}, \
"on": { \
"fill": { \
"iOS": [["highlight", 1]], "android": [["background", 1], ["highlight", 0.5]] \
}, \
"stroke": { \
"iOS": [["border", 1]], "android": [["border", 1]] \
} \
} \
}, \
"thumb": { \
"off": { \
"fill": { \
"iOS": [["background", 1]], "android": [["background", 1]] \
}, \
"stroke": { \
"iOS": [["border", 1]], "android": [["border", 1]] \
} \
}, \
"on": { \
"fill": { \
"iOS": [["background", 1]], "android": [["highlight", 1]] \
}, \
"stroke": { \
"iOS": [["border", 1]], "android": [["border", 1]] \
} \
} \
} \
}
constant kStrokeWidth is 1
constant kDisabledOpacity is 0.5
--
public handler OnSave(out rProperties as Array)
put the empty array into rProperties
put mSwitchIsOn into rProperties["highlight"]
put mShowFrameBorder into rProperties["showBorder"]
end handler
public handler OnLoad(in pProperties as Array)
if "highlight" is among the keys of pProperties then
setSwitch(pProperties["highlight"])
end if
if "showBorder" is among the keys of pProperties then
setShowFrameBorder(pProperties["showBorder"])
end if
end handler
public handler OnCreate() returns nothing
put "native" into mTheme
put getNativeThemeName() into mWidgetTheme
put false into mSwitchIsOn
put true into mShowFrameBorder
put false into mIsPressed
put 0 into mXClick
put false into mMouseHasMoved
put false into mSwitchIsInOnPosition
updateVariables()
end handler
public handler OnPaint() returns nothing
updateVariables()
variable tTransform as optional Transform
variable tScale as optional Number
if not paintGetTransform(tTransform, tScale) then
return
end if
transform this canvas by tTransform
set the stroke width of this canvas to 1/tScale
paintDrawComponent("track")
paintDrawComponent("thumb")
end handler
private handler paintGetTransform(out rTransform as optional Transform, \
out rScale as optional Number) returns Boolean
-- Sanity check
if my width is 0 or my height is 0 then
return false
end if
-- Create a canvas transformation that places the centre of the control at
-- (0, 0), and ensures that the whole control is visible when the radius of
-- the control's "thumb" is 1.
variable tX
variable tY
put (the left of mClickableRect + the right of mClickableRect) / 2 into tX
put (the top of mClickableRect + the bottom of mClickableRect) / 2 into tY
variable tScale
put (the height of mClickableRect / 2) into tScale
variable tTransform
put transform with translation [tX, tY] into tTransform
scale tTransform by [tScale]
-- Flip horizontally for "on" position
if mSwitchIsInOnPosition then
scale tTransform by [-1, 1]
end if
put tScale into rScale
put tTransform into rTransform
return true
end handler
private handler paintGetPath(in pComponent as String) returns Path
-- X position of centre of thumb
variable tCentreX as Number
put 1 - kGeometry["length"][mWidgetTheme] into tCentreX
variable tTrack as Number
variable tStretch as Number
if pComponent is "track" then
put kGeometry["trackwidth"][mWidgetTheme] into tTrack
return rounded rectangle path of rectangle \
[tCentreX - tTrack, -tTrack, tTrack - tCentreX, tTrack] \
with radius tTrack
else if pComponent is "thumb" then
if mIsPressed then
put kGeometry["thumbstretch"][mWidgetTheme] into tStretch
return rounded rectangle path of rectangle \
[tCentreX - 1, -1, tCentreX + 1 + tStretch, 1] with radius 1
else
return circle path centered at point [tCentreX, 0] with radius 1
end if
end if
end handler
private handler paintGetPaint(in pComponent as String, in pType as String) \
returns List
if pType is "stroke" and not mShowFrameBorder then
return []
end if
variable tState as String
if mSwitchIsInOnPosition then
put "on" into tState
else
put "off" into tState
end if
variable tRaw as List
put kPaints[pComponent][tState][pType][mWidgetTheme] into tRaw
variable tPaints as List
put [] into tPaints
variable tPaintInfo as List
repeat for each element tPaintInfo in tRaw
if tPaintInfo[1] is "background" then
put my background paint into tPaintInfo[1]
else if tPaintInfo[1] is "border" then
put my border paint into tPaintInfo[1]
else if tPaintInfo[1] is "highlight" then
put my highlight paint into tPaintInfo[1]
end if
push tPaintInfo onto back of tPaints
end repeat
return tPaints
end handler
private handler paintDrawComponent(in pComponent as String)
variable tPath as Path
put paintGetPath(pComponent) into tPath
variable tBaseOpacity as Number
variable tStrokePaints as List
variable tFillPaints as List
put paintGetPaint(pComponent, "stroke") into tStrokePaints
put paintGetPaint(pComponent, "fill") into tFillPaints
if tStrokePaints is empty and tFillPaints is empty then
return
end if
save state of this canvas
-- When the control is disabled, first paint everything in the background
-- paint, then paint the control over the top with reduced opacity
-- FIXME This is ugly!
if my disabled then
set the paint of this canvas to my background paint
if tFillPaints is not empty then
fill tPath on this canvas
end if
if tStrokePaints is not empty then
stroke tPath on this canvas
end if
put kDisabledOpacity into tBaseOpacity
else
put 1 into tBaseOpacity
end if
-- Loop over the fills, applying them in turn
variable tPaintInfo as List
repeat for each element tPaintInfo in tFillPaints
set the paint of this canvas to tPaintInfo[1]
set the opacity of this canvas to tPaintInfo[2] * tBaseOpacity
fill tPath on this canvas
end repeat
-- Loop over the strokes, applying them in turn
repeat for each element tPaintInfo in tStrokePaints
set the paint of this canvas to tPaintInfo[1]
set the opacity of this canvas to tPaintInfo[2] * tBaseOpacity
stroke tPath on this canvas
end repeat
restore state of this canvas
end handler
----------------------------------------------------------------
-- Other stuff
----------------------------------------------------------------
private handler updateVariables() returns nothing
-- Compute the rectangle for which the widget should respond to clicks.
if my width is 0 or my height is 0 then
put my bounds into mClickableRect
return
end if
variable tMargin as Number
variable tAspectRatio as Number
put kGeometry["margin-px"][mWidgetTheme] into tMargin
put kGeometry["length"][mWidgetTheme] into tAspectRatio
variable tMidWidth as Number
variable tMidHeight as Number
put my width / 2 into tMidWidth
put my height / 2 into tMidHeight
variable tHalfWidth as Number
variable tHalfHeight as Number
put tMidWidth - tMargin into tHalfWidth
put tMidHeight - tMargin into tHalfHeight
if tHalfHeight * tAspectRatio < tHalfWidth then
-- height controlled
put tHalfHeight * tAspectRatio into tHalfWidth
else
-- width controlled
put tHalfWidth / tAspectRatio into tHalfHeight
end if
put rectangle [tMidWidth - tHalfWidth, tMidHeight - tHalfHeight, \
tMidWidth + tHalfWidth, tMidHeight + tHalfHeight] into mClickableRect
end handler
public handler OnMouseMove() returns nothing
if mIsPressed then
variable tXMousePos as Real
variable tChange as Real
variable tMovedRight as Boolean
put true into mMouseHasMoved
put the x of the mouse position into tXMousePos
put tXMousePos - mXClick into tChange
if tChange > 0 then
put true into tMovedRight
else
put false into tMovedRight
end if
if tMovedRight then
put true into mSwitchIsInOnPosition
else
put false into mSwitchIsInOnPosition
end if
redraw all
end if
end handler
public handler OnMouseDown() returns nothing
if my enabled is false then
return
end if
if the click position is within mClickableRect then
put the x of the click position into mXClick
put true into mIsPressed
redraw all
end if
end handler
public handler OnMouseUp() returns nothing
if my enabled is false then
return
end if
variable tPostMessage as Boolean
if mMouseHasMoved then
put mSwitchIsInOnPosition is not mSwitchIsOn into tPostMessage
setSwitch(mSwitchIsInOnPosition)
put false into mMouseHasMoved
else
if the click position is within mClickableRect then
put true into tPostMessage
setSwitch(not(mSwitchIsOn))
else
put false into tPostMessage
end if
end if
if tPostMessage is true then
post "hiliteChanged"
end if
put false into mIsPressed
end handler
public handler OnMouseRelease() returns nothing
if my enabled is false then
return
end if
variable tPostMessage as Boolean
if mMouseHasMoved then
put mSwitchIsInOnPosition is not mSwitchIsOn into tPostMessage
setSwitch(mSwitchIsInOnPosition)
put false into mMouseHasMoved
if tPostMessage is true then
post "hiliteChanged"
end if
put false into mIsPressed
end if
end handler
--------------------------------------------------------------------------------
--
-- Setting Properties
--
--------------------------------------------------------------------------------
constant kKnownThemes is ["iOS", "Android"]
private handler setWidgetTheme(in pTheme as String) returns nothing
if pTheme is mTheme then
return
end if
put pTheme into mTheme
if mTheme is "native" then
put getNativeThemeName() into mWidgetTheme
else
put mTheme into mWidgetTheme
end if
if not (mWidgetTheme is in kKnownThemes) then
throw "invalid theme name '" & mWidgetTheme & "'"
end if
redraw all
end handler
private handler setSwitch(in pIsOn as Boolean) returns nothing
put pIsOn into mSwitchIsOn
put mSwitchIsOn into mSwitchIsInOnPosition
redraw all
end handler
private handler setShowFrameBorder(in pShowBorder as Boolean) returns nothing
put pShowBorder into mShowFrameBorder
redraw all
end handler
end widget