dunbarx wrote: ↑Thu Aug 03, 2023 10:36 pm
Stam.
Aha. I am sure you are right, that the syntax is different for a widget as opposed to the IDE.
And like you, I am excited to be able to make widgets without having to learn LCB. But since I only develop for myself and friends, I will likely never actually need a widget where a group would do.
Craig
Actually on further experimentation, 'in me' can be used with normal groups as well, so it's not specific to script widgets.
The required handlers for script widget (handling openControl, resizeControl, getters and setters for each property, updating appearance when properties change and the business logic handlers) can and arguably should be implemented in a group-based control... Off the top of my head, the biggest differences I found with how I would normally code a group are:
- you have to create the components in script instead of with the IDE. But I experimented with a group and you can certainly do this with a group as well using the same syntax 'in me'.
- instead of referencing a 'control of me' (eg. graphic "myControl" of me) it requires more disambiguation - you store the id of the control created in a script local variable and reference it with control id sMyControlID - interestingly this works just fine with normal groups as well.
- Metadata required for the widget and the property inspector for the widget - clearly you can't use these with a normal group. The PI metadata is the bit I found most confusing as I struggled to find any documentation on how to apply this to stuff not in the lesson referenced above, but which really isn't hard.
- There is no flexibility for error in the script widget - anything that goes wrong will make the whole thing fail and there's no help debugging it.
I recently posted a
tri-state switch widget created as a script widget, based on a group control I had created before that. While that did require some re-working of the initial code used for the group-control, it was actually fairly straightforward using this re-worked widget code back into a group. In practice, you would create an empty group, assign the adapted code as the script (or a behaviour) of the new empty group and send openControl to it to initialise it.
Resizing groups can be a pain, but can be fixed to be the same as a widget by setting the
clipsToRect of the group to true and actually you can't tell the difference from the script widget other than it doesn't have settings in the Property Inspector, doesn't have an icon in the Tools palette and can't be packaged as an extension for sharing.
With my tri-state widget as an example, here is how I modified the script widget's code to work for a normal group (the code for my script widget can be viewed/downloaded at
https://github.com/stam66/tristate):
Code: Select all
local sHasCreatedVisuals, sMouseX, sClickedIndicator
local sBackroundID, sIndicatorID
########################### PROPERTIES ###########################
// rightColor
getProp rightColor
local tColor
put the rightColor of me into tColor
if tColor is empty then put "#C90076" into tColor
return tColor
end rightColor
setProp rightColor pColor
set the rightColor of me to pColor
updateVisualControls
end rightColor
// leftColor
getProp leftColor
local tColor
put the leftColor of me into tColor
if tColor is empty then put "#2986CC" into tColor
return tColor
end leftColor
setProp leftColor pColor
set the leftColor of me to pColor
updateVisualControls
end leftColor
// centerColor
getProp centerColor
local tColor
put the centerColor of me into tColor
if tColor is empty then put "#F4F6F7" into tColor
return tColor
end centerColor
setProp centerColor pColor
set the centerColor of me to pColor
updateVisualControls
end centerColor
// indicatorColor
getProp indicatorColor
local tColor
put the indicatorColor of me into tColor
if tColor is empty then put "#E6E8E8" into tColor
return tColor
end indicatorColor
setProp indicatorColor pColor
set the indicatorColor of me to pColor
updateVisualControls
end indicatorColor
// switchValue
getProp switchValue
local tValue
put the switchValue of me into tValue
if tValue is empty then put 0 into tValue
return tValue
end switchValue
setProp switchValue pValue
set the switchValue of me to pValue
updateIndicatorForValue pValue
end switchValue
########################### /PROPERTIES ##########################
on openControl
createVisualControls
layoutVisualControls
updateVisualControls
end openControl
on resizeControl
layoutVisualControls
end resizeControl
private command createVisualControls
if sHasCreatedVisuals then exit createVisualControls
lock screen
set the width of me to 70
set the height of me to 35
set the clipsToRect of me to true
if there is not a graphic "background" of me then create graphic "background" in me
put the id of graphic "background" of me into sBackroundID
set the style of control id sBackroundID to "roundRect"
set the opaque of control id sBackroundID to true
set the linesize of control id sBackroundID to 1
if there is not a graphic "indicator" of me then create graphic "indicator" in me
put the id of graphic "indicator" of me into sIndicatorID
set the style of control id sIndicatorID to "oval"
set the opaque of control id sIndicatorID to true
set the linesize of control id sIndicatorID to 1
set the backgroundColor of control id sIndicatorID to "#E6E8E8"
local tInnerGlow
put "normal" into tInnerGlow["blendMode"]
put 255,255,255 into tInnerGlow["color"]
put "gaussian" into tInnerGlow["filter"]
put 255 into tInnerGlow["opacity"]
put 255 into tInnerGlow["range"]
put 20 into tInnerGlow["size"]
put "edge" into tInnerGlow["source"]
put 0 into tInnerGlow["spread"]
set the innerGlow of control id sIndicatorID to tInnerGlow
set the switchValue of me to "center"
put true into sHasCreatedVisuals
layoutVisualControls
end createVisualControls
private command layoutVisualControls
if not sHasCreatedVisuals then exit layoutVisualControls
local tRect, tDiameter, tLength
put the rect of me into tRect
add 2 to item 1 of tRect
add 2 to item 2 of tRect
subtract 2 from item 3 of tRect
subtract 2 from item 4 of tRect
set the rect of control id sBackroundID to tRect
put item 4 of tRect - item 2 of tRect into tDiameter
set the width of control id sIndicatorID to tDiameter - 4
set the height of control id sIndicatorID to tDiameter - 4
set the roundRadius of control id sBackroundID to tDiameter
if the mouse is up then updateIndicatorForValue the switchValue of me
end layoutVisualControls
private command updateVisualControls
if not sHasCreatedVisuals then exit updateVisualControls
switch the switchValue of me
case "Left"
set the backgroundColor of control id sBackroundID to the leftColor of me
break
case "Center"
set the backgroundColor of control id sBackroundID to the centerColor of me
break
case "Right"
set the backgroundColor of control id sBackroundID to the rightColor of me
break
end switch
set the backgroundColor of control id sIndicatorID to the indicatorColor of me
end updateVisualControls
private command updateIndicatorForValue pValue
local tX
switch pValue
case "Left"
set the backgroundColor of control id sBackroundID to the leftColor of me
put the left of control id sBackroundID + 3 + the width of control id sIndicatorID/2 into tX
move control id sIndicatorID to tX, item 2 of the loc of control id sBackroundID in 200 milliseconds
set the left of control id sIndicatorID to the left of control id sBackroundID + 2
break
case "Center"
set the backgroundColor of control id sBackroundID to the centerColor of me
move control id sIndicatorID to the loc of control id sBackroundID in 200 milliseconds
break
case "Right"
set the backgroundColor of control id sBackroundID to the rightColor of me
put the right of control id sBackroundID - 3 - the width of control id sIndicatorID/2 into tX
move control id sIndicatorID to tX, item 2 of the loc of control id sBackroundID in 200 milliseconds
set the right of control id sIndicatorID to the right of control id sBackroundID -2
break
end switch
end updateIndicatorForValue
on mouseDown
if "groupbehavior" is in the long name of the target then exit to top
if the mouseLoc is within the rect of control id sIndicatorID then
put true into sClickedIndicator
put the mouseH into sMouseX
end if
pass mouseDown
end mouseDown
on mouseUp pKey
local tSegmentClicked, tRect, tR1, tR2, tR3, t3
if "groupbehavior" is in the long name of the target then exit to top
if sClickedIndicator then // drag indicator 1 step in drag direction
local tValue
put the switchValue of me into tValue
if the mouseH - sMouseX > 0 and tValue is "Left" then // go right
put "Center" into tValue
else if the mouseH - sMouseX > 0 and tValue is "Center" then // go right
put "Right" into tValue
else if the mouseH - sMouseX < 0 and tValue is "Right" then //go left
put "Center" into tValue
else if the mouseH - sMouseX < 0 and tValue is "Center" then //go left
put "Left" into tValue
end if
set the switchValue of me to tValue
else // establish 3 click zones to see if left, centre or right
put the width of control id sBackroundID / 3 into t3
put the rect of control id sBackroundID into tR1
put item 1 of tR1 + t3 into item 3 of tR1
put tR1 into tR2
add t3 to item 1 of tR2
add t3 to item 3 of tR2
put tR2 into tR3
add t3 to item 1 of tR3
add t3 to item 3 of tR3
// apply value based on click zone
if the mouseLoc is within tR1 then
set the switchValue of me to "Left"
else if the mouseLoc is within tR2 then
set the switchValue of me to "Center"
else if the mouseLoc is within tR3 then
set the switchValue of me to "Right"
end if
end if
put false into sClickedIndicator
put empty into sMouseX
end mouseUp
on mouseRelease
local tValue
if "groupbehavior" is in the long name of the target then exit to top
put the switchValue of me into tValue
if the mouseH - sMouseX > 0 and tValue is "Left" then // go right
put "Center" into tValue
else if the mouseH - sMouseX > 0 and tValue is "Center" then // go right
put "Right" into tValue
else if the mouseH - sMouseX < 0 and tValue is "Right" then //go left
put "Center" into tValue
else if the mouseH - sMouseX < 0 and tValue is "Center" then //go left
put "Left" into tValue
end if
set the switchValue of me to tValue
put false into sClickedIndicator
put empty into sMouseX
end mouseRelease
I put this code in the script of a substack called
groupBehavior, and can create a new tri-state switch as a normal group with:
Code: Select all
on mouseUp pButtonNumber
create group "tristate1"
set the behavior of group "tristate1" to the long id of stack "groupBehavior"
send "openControl" to group "tristate1"
end mouseUp
This creates a group that is indistinguishable from the widget but has no Property Inspector panel and doesn't appear in the Tools palette - but is largely the same code as the script widget (other than setting the clipsToRect to true and removing the metadata declarations). Given how finicky script widgets are, you are almost forced to use more disciplined code which is arguably a good thing generally
The bit still missing documentation-wise is more guidance about script and property metadata... but I do like script widgets because they are encapsulated, shareable and easily re-used. But of course they don't do anything functionally that a group can't.
S.
PS: One downside to script widgets vs groups: I just experimented saving a stack with the above group code and the same as a script widget. On quitting LC and re-opening the stack, the group control appears instantly, while the script widget control takes 1-2 seconds to appear (not sure if that's just my bad code or an inherent issue with script widgets - or indeed if it's something that will improve as the techology matures).