forked from livecode/livecode-ide
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.mlc
More file actions
543 lines (470 loc) · 17.7 KB
/
main.mlc
File metadata and controls
543 lines (470 loc) · 17.7 KB
1
// Widget declaration//// Widgets are named using reverse-DNS notation so if, for example, your// domain is "www.example.com", you might name your widget:// com.example.widgets.my_awesome_widget//widget com.livecode.extensions.livecode.button// Widget metadata//// Currently, three metadata tokens are recognised://// author - the name of the person who created the widget// version - the release number for this widget// title - human-readable name for the widget//metadata author is "LiveCode Ltd"metadata version is "1.0.0"metadata title is "Example Button"// Dependency declarations//// These lines specify the names of other modules that this widget depends// on. Both of these modules are built into the engine - dependencies on// other external modules do not currently function.use com.livecode.canvasuse com.livecode.widget// Property declarations//// Each publically-accessible property of your widget is declared here.// In addition to the name of the property, handlers for property// getting and setting can be specified (if your property is simple,// either or both of these can simply be the name of the instance// variable to modify).//// Currently, a setter for the property is mandatory though this// restriction will be relaxed in the near future to allow for// read-only properties.//// Types for properties are inferred from the return type of the// getter, the parameter type of the setter or the declared type of// the variable. These types should be the same as they are used to// create the property inspector for your widget.//// Properties (along with events) are the interface between LiveCode// Builder and LiveCode Script so you need to ensure that any types// returned from your getters or accepted by your setters are usable// from Script (e.g. "pointer" is not valid).//property primaryColor get getPrimaryColor set setPrimaryColorproperty secondaryColor get getSecondaryColor set setSecondaryColorproperty textColor get getTextColor set setTextColorproperty borderColor get getBorderColor set setBorderColorproperty paintStyle get mPaintStyle set setPaintStyleproperty cornerRadius get mCornerRadius set setCornerRadiusproperty borderWidth get mBorderWidth set setBorderWidthproperty animate get mAnimate set setAnimateproperty label get mLabel set setLabelproperty textFont get mTextFont set setTextFontproperty textSize get mTextSize set setTextSizeproperty iconPath get mIconPath set setIconPath// Event declarations//// These declare the events that the widget will generate - it is these// events that get dispatched to the script attached to a widget instance.//// As this is an example button, we'll declare a "click" event////event // Event handlers//// These handlers are called automatically by the engine in response to// events such as mouse movement, keyboard input or a redraw request.//// For a list of all the events sent by the engine and details of their// purpose, take a look at SkeletonWidget.mlc//// As the event handlers must be accessible to the engine, they should be// declared as public.public handler OnCreate() as undefined // Set default values for all of the instance variables put "linear gradient" into mPaintStyle put color [ 0.7, 0.7, 1.0 ] into mPrimaryColor put color [ 0.5, 0.5, 1.0 ] into mSecondaryColor put color [ 0.0, 0.0, 0.0 ] into mTextColor put color [ 0.7, 0.1, 0.1 ] into mBorderColor put 10.0 into mCornerRadius put 3.0 into mBorderWidth put 1.0 into mScale put 0.0 into mRotation put 0.5 into mRotationDelta put -0.001 into mScaleDelta put false into mPressed put false into mHover put "Example Button" into mLabel // Set the paint correctly for this widget updatePaint(mPaintStyle) // Enable animation setAnimate(true)end handlerpublic handler OnPaint() as undefined // Create a rounded rectangle path at the size of this widget // // "my bounds" is a rectangle with an origin at 0,0 and with a // width and height equal to the width and height of the widget. // // There also exists "my frame" which is the position and size of // the widget relative to its parent and "my rectangle" which is // the same but relative to the containing card. // variable tOuterRectPath as Path put rounded rectangle path of my bounds with radius mCornerRadius into tOuterRectPath // The inner rectangle is more bothersome as it needs to be reduced by // the border width on all four sides variable tInnerRect as Rectangle variable tInnerRectPath as Path put my bounds into tInnerRect add mBorderWidth to the x of tInnerRect add mBorderWidth to the y of tInnerRect subtract 2*mBorderWidth from the width of tInnerRect subtract 2*mBorderWidth from the height of tInnerRect put rounded rectangle path of tInnerRect with radius mCornerRadius into tInnerRectPath // Scale the canvas according to the animation settings // // "this canvas" is the drawing canvas that is currently active - // it is set up automatically before the call to OnPaint. // variable tAnimate as boolean put (mAnimate and not in edit mode) into tAnimate if tAnimate then // A translation will be required to keep things centred translate this canvas by [ 0.5*(1-mScale)*(the width of my bounds), 0.5*(1-mScale)*(the height of my bounds) ] scale this canvas by [ mScale, mScale ] end if // Draw the outer rectangle using the fill color, darkening when clicked // and lightening it when hovered. variable tBorderColor if mPressed then put multiplyColor(mBorderColor, 0.75) into tBorderColor else if mHover then put multiplyColor(mBorderColor, 1.50) into tBorderColor else put mBorderColor into tBorderColor end if set the paint of this canvas to solid paint with tBorderColor fill tOuterRectPath on this canvas // Draw the inner rectangle using the cached paint set the paint of this canvas to mPaint fill tInnerRectPath on this canvas // Create the font used for drawing the text. Inherit font information // from the canvas if the properties aren't explicitly defined. variable tTextFontName as string variable tTextFontSize as real variable tTextFont as optional Font if mTextFont is defined then put mTextFont into tTextFontName else put the name of the font of this canvas into tTextFontName end if if mTextSize is defined then put mTextSize into tTextFontSize else put the size of the font of this canvas into tTextFontSize end if put font tTextFontName at size tTextFontSize into tTextFont // The position of the icon and label depend on their sizes. First, // calculate the bounds of each and the padding between them. variable tIconRect as Rectangle variable tLabelRect as Rectangle variable tPadding as real if mIconImage is defined then put rectangle [ 0, 0, the width of mIconImage, the height of mIconImage ] into tIconRect else put rectangle [ 0, 0, 0, 0 ] into tIconRect end if if mLabel is defined then put the bounds of text mLabel with tTextFont into tLabelRect else put rectangle [ 0, 0, 0, 0 ] into tLabelRect end if if (the width of tIconRect) > 0 and (the width of tLabelRect) > 0 then put 10 into tPadding else put 0 into tPadding end if // Next, position each rectangle correctly variable tTotalWidth as real variable tTotalHeight as real variable tXOffset as real variable tYOffset as real put (the width of tIconRect) + tPadding + (the width of tLabelRect) into tTotalWidth put the max of (the height of tIconRect) and (the height of tLabelRect) into tTotalHeight put ((the width of tInnerRect) - tTotalWidth)/2 into tXOffset put ((the height of tInnerRect) - tTotalHeight)/2 into tYOffset set the top of tIconRect to tYOffset set the top of tLabelRect to 0 set the height of tLabelRect to the height of tInnerRect set the left of tIconRect to tXOffset set the left of tLabelRect to tXOffset + (the width of tIconRect) + tPadding // Draw the icon if mIconImage is defined then // We're going to mess with the canvas, so save its state save state of this canvas // Translate to the center of the icon translate this canvas by [ (the x of tIconRect) + (the width of tIconRect)/2, (the y of tIconRect) + (the height of tIconRect)/2 ] // Rotate the canvas according to the animation parameters if mAnimate and not in edit mode then rotate this canvas by mRotation end if // Draw the icon at the correct location (adjusted for the // previous translation). set the left of tIconRect to -(the width of tIconRect)/2 set the top of tIconRect to -(the height of tIconRect)/2 draw mIconImage into tIconRect of this canvas // Restore the canvas transform restore state of this canvas end if // Draw the label if mLabel is defined then set the paint of this canvas to solid paint with color [ 0, 0, 0 ] fill text mLabel at center of tLabelRect on this canvas end ifend handlerpublic handler OnMouseEnter() as undefined put true into mHover redraw allend handlerpublic handler OnMouseLeave() as undefined put false into mHover redraw allend handlerpublic handler OnMouseDown() as undefined put true into mPressed updatePaint(mPaintStyle)end handlerpublic handler OnMouseUp() as undefined // The widget is no longer pressed put false into mPressed updatePaint(mPaintStyle) // A mouse up event (as opposed to a cancel) means that the button has // been clicked. Send a message to the script so it can handle it. dispatch "buttonClicked" to my script objectend handlerpublic handler OnTimer() as undefined // Set the new scale factor for the widget add mScaleDelta to mScale // Set the new rotation for the icon add mRotationDelta to mRotation // If the widget is too small or too large, reverse the pulse direction if mScale > 1.0 or mScale < 0.9 then put -mScaleDelta into mScaleDelta end if // Timers are one-shot and need re-triggered schedule timer in 1/60 seconds // Update the widget redraw allend handlerpublic handler OnGeometryChanged() as undefined // Update the paint so the gradient is scaled correctly updatePaint(mPaintStyle)end handler// Private instance variables//// These variables are private to the widget (i.e they cannot be accessed// by code outside the widget). Each copy ("instance") of the widget has// its own copy of these variables - they are not shared between widgets// of the same type.//private variable mPaintStyle as stringprivate variable mPaint as Paintprivate variable mPrimaryColor as Colorprivate variable mSecondaryColor as Colorprivate variable mTextColor as Colorprivate variable mBorderColor as Colorprivate variable mCornerRadius as realprivate variable mBorderWidth as realprivate variable mAnimate as booleanprivate variable mScale as realprivate variable mScaleDelta as realprivate variable mRotation as realprivate variable mRotationDelta as realprivate variable mPressed as booleanprivate variable mHover as booleanprivate variable mLabel as optional stringprivate variable mTextFont as optional stringprivate variable mTextSize as optional integerprivate variable mIconPath as optional stringprivate variable mIconImage as optional Image// Private handlers//// These handlers are not accessible to code outside the widget.//// Although the "private" keyword is used on these handlers, it is not// strictly necessary - handlers and variables are private by default// and need to be explicitly declared as public.//private handler stringToColor(in pString as string) as Color variable tRed as real variable tGreen as real variable tBlue as real variable tAlpha as real // Split the input string into its components // // In LiveCode Builder, "list" is now a proper type and it is not // necessary to use a particular character as an item separator. // // This line of code converts a Script-style comma-delimited list // into a "proper" list. // // The chunk type to use for lists is "element". // variable tComponentList as list split pString by "," into tComponentList // Check that the number of components is correct. // // Note that in LiveCode Builder, the type of a variable need not // be specified - it defaults to "optional any" indicating that // the variable can hold any type (or even nothing at all). // variable tComponentCount put the number of elements in tComponentList into tComponentCount if tComponentCount is not 3 and tComponentList is not 4 then // Invalid number of components detected throw "Invalid color" end if // Interpret the RGB components of the color // // LiveCode Builder does not automatically convert between numbers // and strings (or between any other types - integers and boolean // values are distinct too). // 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 // Use the alpha component, if specified // // "if" statements work as they do in LiveCode Script except that // the "end if" clause is always mandatory (i.e single-line "if" // statements do not exist). // if tComponentCount is 4 then put (element 4 of tComponentList) parsed as number into tAlpha else put 1.0 into tAlpha end if // Create the color using a list of component values // // The [ ... ] syntax is used to create a list. // return color [ tRed, tGreen, tBlue, tAlpha ]end handlerprivate handler colorToString(in pColor as Color) as string variable tString as string // Convert the numeric color component values into a string put ((the red of pColor) formatted as string) into tString put "," & ((the green of pColor) formatted as string) after tString put "," & ((the blue of pColor) formatted as string) after tString return tStringend handlerprivate handler updatePaint(in pStyle as string) as undefined // Switch statements are not currently implemented so a chain of "if" // statements needs to be used instead. if pStyle is "solid color" then // Darken the button when pressed if mPressed then put solid paint with multiplyColor(mPrimaryColor, 0.5) into mPaint else put solid paint with mPrimaryColor into mPaint end if else if pStyle is "linear gradient" then // Linear gradients have directions. We're going to be // complicated and have the gradient run from the top left to // the bottom right of the widget. // // First, set up the basic ramp. If the button is pressed, // reverse the direction of the two colors. variable tStops as list if mPressed then put [ gradient stop at 0.0 with mSecondaryColor, gradient stop at 1.0 with mPrimaryColor ] into tStops else put [ gradient stop at 0.0 with mPrimaryColor, gradient stop at 1.0 with mSecondaryColor ] into tStops end if put linear gradient with ramp tStops into mPaint // Set the "to" and "via" points to get the diagonal gradient variable tWidth as real variable tHeight as real put the width of my bounds into tWidth put the height of my bounds into tHeight scale mPaint by [ tWidth, tHeight ] else throw "Unknown paint style set" end if // Paint was updated successfully put pStyle into mPaintStyle // A re-draw will be required redraw allend handlerprivate handler multiplyColor(in pColor as Color, in pFactor as real) as Color variable tRed as real variable tGreen as real variable tBlue as real variable tAlpha as real put (the red of pColor) * pFactor into tRed put (the green of pColor) * pFactor into tGreen put (the blue of pColor) * pFactor into tBlue put the alpha of pColor into tAlpha return color [ tRed, tGreen, tBlue, tAlpha ]end handlerprivate handler getPrimaryColor() as string return colorToString(mPrimaryColor)end handlerprivate handler setPrimaryColor(in pColor as string) as undefined put stringToColor(pColor) into mPrimaryColor updatePaint(mPaintStyle)end handlerprivate handler getSecondaryColor() as string return colorToString(mSecondaryColor)end handlerprivate handler setSecondaryColor(in pColor as string) as undefined put stringToColor(pColor) into mSecondaryColor updatePaint(mPaintStyle)end handlerprivate handler getTextColor() as string return colorToString(mTextColor)end handlerprivate handler setTextColor(in pColor as string) as undefined put stringToColor(pColor) into mTextColor redraw allend handlerprivate handler getBorderColor() as string return colorToString(mBorderColor)end handlerprivate handler setBorderColor(in pColor as string) as undefined put stringToColor(pColor) into mBorderColor redraw allend handlerprivate handler setPaintStyle(in pStyle as string) as undefined updatePaint(pStyle)end handlerprivate handler setCornerRadius(in pRadius as real) as undefined put pRadius into mCornerRadius redraw allend handlerprivate handler setBorderWidth(in pWidth as real) as undefined put pWidth into mBorderWidth redraw allend handlerprivate handler setAnimate(in pAnimate as boolean) as undefined put pAnimate into mAnimate if mAnimate then schedule timer in 1/60 seconds else cancel timer end ifend handlerprivate handler setLabel(in pLabel as string) as undefined put pLabel into mLabel redraw allend handlerprivate handler setTextFont(in pTextFont as string) as undefined put pTextFont into mTextFont redraw allend handlerprivate handler setTextSize(in pTextSize as integer) as undefined put pTextSize into mTextSize redraw allend handlerprivate handler setIconPath(in pIconPath as string) as undefined put pIconPath into mIconPath put image from file pIconPath into mIconImageend handlerend widget