-- INTEGER VALUE SLIDER -- -- -- © May 2001, James Newton -- ---------------------------------------------------------------------- -- This behavior uses Imaging Lingo to create a slider similar to that -- used by Macromedia software on Windows. ---------------------------------------------------------------------- -- 040326 JN: sliderValue in getPDL() set to a string to allow it to -- contain an expression which will be evaluated on -- mInitialize(). ---------------------------------------------------------------------- -- PROPERTY DECLARATIONS -- property spriteNum -- author-defined parameters property minValue -- Minimum value returned by slider property maxValue -- Maximum value returned by slider property sliderValue -- Current value of slider property pixelSteps -- Number of discrete pixels along slider property borderHeight -- Space above slider property revertDistance -- Slider reverts if cursor is beyond this property textSprite -- Text sprite to update when value chosen property callHandler -- Callback handler when value is chosen property callObject -- Location of the handler (#movie|#score...) -- internal properties property pSprite -- sprite(spriteNum property pMember -- 0riginal member of pSprite property pRegPoint -- regPoint of pMember property pBitmap -- Temporarty Bitmap used instead of pMember property pUpImage -- Image of pMember property pDownImage -- Modified image, in pressed state property pTempText -- Temporary text member used for figures property pSliderImage -- 4-bit image object of slider thumb property pBackingImage -- 4-bit image object of slider bar property pOpenImage -- 4-bit image object with button showing property pOffset -- Distance from top of slider to regPoint property pCenterH -- locH of center of slider bar property pUnitsPerPixel -- Number of units modified per pixel moved property pClickV -- Vertical position of mouse click property pCurrentV -- Last recorded vertical position of mouse property pCurrentValue -- Value to be adopted if mouse is released -- SPRITE EVENTS -- on beginSprite(me) me.mInitialize() end beginSprite on endSprite(me) me.mEraseTemporaryMembers() end endSprite -- EVENT HANDLERS -- on mouseDown(me) me.mOpenSlider() end mouseDown on exitFrame(me) if pClickV then if the mouseDown then me.mUpdate() else me.mCloseSlider() end if end if end exitFrame -- PUBLIC METHODS -- on GetReference(me, anID, aReturnList) ------------------------------- -- INPUT: may be a symbol or a list -- may be a list or -- OUTPUT: Returns a pointer to this behavior instance or updates -- aReturnList -------------------------------------------------------------------- return me.mProvideData(#instance, anID, aReturnList) end GetReference on GetValue(me, anID, aReturnList) ----------------------------------- -- INPUT: may be a symbol or a list -- may be a list or -- OUTPUT: Returns the value of or updates returnList -------------------------------------------------------------------- return me.mProvideData(#value, anID, aReturnList) end GetValue on SetValue(me, anID, aNewValue) ------------------------------------- -- INPUT: may be a symbol or a list -- should be an integer -- ACTION: Updates if it is within the ascribed limits -- OUTPUT: Returns TRUE if the new value is adopted, FALSE if not -------------------------------------------------------------------- return me.mAcceptValue(anID, aNewValue) end SetValue -- PRIVATE METHODS -- on mOpenSlider(me) --------------------------------------------------- -- SENT BY mouseDown() -- ACTION: Creates a temporary image pOpenImage which shows the -- button and the slider backing bar in the appropriate -- relative positions. -------------------------------------------------------------------- pClickV = the mouseLoc tClickedLocV = pSprite.mapStageToMember(pClickV).locV tClickedLocV = tClickedLocV - pMember.height pClickV = pClickV.locV -- Place button in appropriate position on a fixed image pOpenImage = pBackingImage.duplicate() -- Calculate slider position tTop = borderHeight - 6 - 3 -- height of sliderImage - 3 pOffset = pixelSteps+integer((minValue-sliderValue)/pUnitsPerPixel) pOffset = pOffset + tTop - tClickedLocV tCopyArea = pDownImage.rect tPasteArea = tCopyArea + [0, pOffset, 0, pOffset] pOpenImage.copyPixels(pDownImage, tPasteArea, tCopyArea) pCurrentValue = #none -- Show slider in front of all other sprites pSprite.locZ = the maxInteger me.mUpdate() end mOpenSlider on mUpdate(me) ------------------------------------------------------- -- SENT BY exitFrame() while the slider is showing -- ACTION: creates a new tActiveImage each time the value of the -- slider changes. The position of the slider thumb and the -- text value will be updated. -------------------------------------------------------------------- if pCurrentV = the mouseV then -- The mouse hasn't moved exit end if tRevert = FALSE if abs(the mouseH - pCenterH) > revertDistance then if pCurrentValue = sliderValue then -- The slider has already been reverted exit else if not rollover(spriteNum) then -- The cursor is too far from the slider tRevert = TRUE end if end if if tRevert then pCurrentValue = sliderValue else pCurrentV = the mouseV tDeltaV = pClickV - pCurrentV -- Ensure that the slider's value remains within the set limits tDeltaValue = pUnitsPerPixel * tDeltaV tValue = integer(sliderValue + tDeltaValue) tValue = max(minValue, min(tValue, maxValue)) if pCurrentValue = tValue then -- The mouse has moved but the value hasn't changed exit end if pCurrentValue = tValue end if -- Adjust the value of tDeltaV if necessary tDeltaV = (pCurrentValue - minValue - sliderValue) / pUnitsPerPixel tSliderV = pixelSteps - (sliderValue / pUnitsPerPixel) -- Create a new active image tActiveImage = pOpenImage.duplicate() -- Place the slider and its text value on a dynamic image tCopyArea = pSliderImage.rect tDownSet = tSliderV + pMember.height - tDeltaV - 3 -- tweak tInset = pMember.width + 2 tPasteArea = tCopyArea.offset(tInset, tDownSet) tActiveImage.copyPixels(pSliderImage, tPasteArea, tCopyArea) -- Show the value of the slider pTempText.text = string(pCurrentValue) tTextImage = pTempText.image tCopyArea = tTextImage.rect tDownSet = tSliderV + pMember.height - tDeltaV - 6 -- tweak tInset = pMember.width + 16 tPasteArea = tCopyArea.offset(tInset, tDownSet) tActiveImage.copyPixels(tTextImage, tPasteArea, tCopyArea) -- Make tActiveImage visible in the sprite pBitmap.image = tActiveImage pBitmap.regPoint = pRegPoint + [0, pOffset] end mUpdate on mCloseSlider(me) -------------------------------------------------- -- SENT BY exitFrame() when the mouse button is released -- ACTION: Validates the selected value -------------------------------------------------------------------- -- Update the sliderValue property and the text sprite's display sliderValue = pCurrentValue me.mUpdateTextSprite() -- Revert to the initial image and locZ pBitmap.image = pUpImage pBitmap.regPoint = pRegPoint pSprite.locZ = spriteNum -- Call the appropriate handler if symbolP(callHandler) then case callObject of #movie: sendSprite(0, callHandler, sliderValue) #sprite: call(callHandler, pSprite.scriptInstanceList, sliderValue) #sendAllSprites: sendAllSprites(callHandler, sliderValue) #actorList: call(callHandler, the actorList, sliderValue) end case end if -- Release memory used while the mouse was down pOpenImage = 0 pClickV = 0 pCurrentV = 0 end mCloseSlider on mUpdateTextSprite(me) --------------------------------------------- -- SENT BY mAcceptValue() and mCloseSlider() -- ACTION: Validates the selected value -------------------------------------------------------------------- if textSprite then textMember = sprite(textSprite).member case textMember.type of #button, #field, #text: textmember.text = string(sliderValue) end case end if end mUpdateTextSprite -- Initialization on mInitialize(me) --------------------------------------------------- -- SENT BY beginSprite() -- ACTION: Initializes the sprite and this behavior's properties. -------------------------------------------------------------------- pSprite = sprite(spriteNum) -- Prepare the sprite pSprite.ink = 8 -- #matte pSprite.stretch = FALSE -- Won't work with stretched... pSprite.flipH = FALSE -- ... flipped... pSprite.flipV = FALSE pSprite.rotation = 0 -- ... or rotated sprites (Merci SP). tCallObjects = me.mGetLocalizedString(#callObjects, FALSE) callObject = tCallObjects.getOne(callObject) -- Use a temporary member so as not to modify the original pMember = pSprite.member pRegPoint = pMember.regPoint pUpImage = pMember.image pBitmap = new(#bitmap) pBitmap.name = "Temp Bitmap member for Slider behavior" pBitmap.image = pUpImage pBitmap.regPoint = pRegPoint pSprite.member = pBitmap -- Create image sources for widget elements tButtonWidth = pUpImage.width tButtonHeight = pUpImage.height pSliderImage = me.mGetSliderImage() pTempText = me.mGetTempTextMember() pBackingImage = me.mGetBackingImage() pDownImage = me.mGetDownImage() -- Determine vertical center of slider pCenterH = pSprite.left + tButtonWidth + 9 + pTempText.width / 2 -- Determine ratio to use (minValue must be less than maxValue) if minValue > maxValue then temp = minValue minValue = maxValue maxValue = temp end if pUnitsPerPixel = float(maxValue - minValue) / pixelSteps -- Ensure slider value is valid, and inform textSprite sliderValue = value(sliderValue) pCurrentValue = max(minValue, min(sliderValue, maxValue)) me.mCloseSlider() end mInitialize -- Creating image sources for widget elements on mGetSliderImage(me) ------------------------------------------------ -- CALLED by mInitialize() -- ACTION: Creates a hard-coded image of a slider thumb -- OUTPUT: an image object -------------------------------------------------------------------- sliderImage = image(14, 6, 4, #grayscale) tOptions = [:] tOptions[#shapeType] = #rect tOptions[#color] = rgb(0, 0, 0) sliderImage.fill(0, 0, 14, 6, tOptions) tOptions[#color] = rgb(187, 187, 187) sliderImage.fill(0, 0, 13, 5, tOptions) tOptions[#color] = rgb(119, 119, 119) sliderImage.fill(1, 1, 13, 5, tOptions) tOptions[#color] = rgb(238, 238, 238) sliderImage.fill(1, 1, 12, 4, tOptions) tOptions[#color] = rgb(187, 187, 187) sliderImage.fill(2, 2, 12, 4, tOptions) return sliderImage end mGetSliderImage on mGetTempTextMember(me) -------------------------------------------- -- CALLED by mInitialize() -- ACTION: Creates a temporary text member whose image will be used -- as the source for the slider value. -- OUTPUT: a text member -------------------------------------------------------------------- pTempText = new(#text) pTempText.name = "Temp Text member for Slider behavior" if the platform contains "Mac" then pTempText.font = "Geneva" pTempText.fontSize = 9 else pTempText.font = "Arial" pTempText.fontSize = 9 end if testString = string(maxValue) pTempText.text = testString valueWidth = pTempText.charPosToLoc(testString.char.count + 1).locH testString = string(minValue) pTempText.text = testString tempWidth = pTempText.charPosToLoc(testString.char.count + 1).locH if valueWidth < tempWidth then valueWidth = tempWidth end if pTempText.width = valueWidth + 1 pTempText.alignment = #right return pTempText end mGetTempTextMember on mGetBackingImage(me) ---------------------------------------------- -- CALLED by mInitialize() -- ACTION: Creates an image object sufficiently large to show all -- the widget elements. The grooved backing bar for the -- slider is already shown. -- OUTPUT: an image object -------------------------------------------------------------------- tButtonWidth = pUpImage.width tButtonHeight = pUpImage.height -- Create large image to receive pop-out slider tBackingWidth = tButtonWidth + 18 + pTempText.width tBackingHeight = pixelSteps + (borderHeight * 2) - 4 tImageHeight = tBackingHeight + tButtonHeight * 2 -- Draw backing bar panel with tButtonHeight/2 margin above and -- below. Use fill() since draw() does not work on 4-bit image -- objects. tBackingImage = image(tBackingWidth, tImageHeight, 4, #grayscale) tOptions = [:] tOptions[#shapeType] = #rect tOptions[#color] = rgb(0, 0, 0) tFillTop = tButtonHeight - borderHeight + 2 tFillBottom = tBackingHeight + tFillTop tFillRect = rect(tButtonWidth,tFillTop,tBackingWidth,tFillBottom) tBackingImage.fill(tFillRect, tOptions) tOptions[#color] = rgb(187, 187, 187) tFillRect = tFillRect.inflate(-1, -1) tBackingImage.fill(tFillRect, tOptions) -- Create the groove tHigh = tFillTop + borderHeight - 2 tLow = tFillTop + tBackingHeight - borderHeight + 2 tOptions[#color] = rgb(238, 238, 238) tFillArea = rect(tButtonWidth + 7, tHigh, tButtonWidth + 11, tLow) tBackingImage.fill(tFillArea, tOptions) tOptions[#color] = rgb(0, 0, 0) tFillArea = rect(tButtonWidth+7, tHigh, tButtonWidth+10, tLow-1) tBackingImage.fill(tFillArea, tOptions) tOptions[#color] = rgb(187, 187, 187) tFillArea = rect(tButtonWidth+8, tHigh+1, tButtonWidth+10, tLow-1) tBackingImage.fill(tFillArea, tOptions) tOptions[#color] = rgb(119, 119, 119) tFillArea = rect(tButtonWidth+8, tHigh+1, tButtonWidth+9, tLow-2) tBackingImage.fill(tFillArea, tOptions) return tBackingImage end mGetBackingImage on mGetDownImage(me) ------------------------------------------------- -- CALLED by mInitialize() -- ACTION: Creates a version of the initial member image, with the -- border rotated 180° and the center move 1 pixel down and -- to the right -- OUTPUT: an image object -------------------------------------------------------------------- -- Invert arrow button so that its frame is shaded inwards tSourceImage = pUpImage tDownImage = tSourceImage.duplicate() tButtonWidth = tDownImage.width tButtonHeight = tDownImage.height tCopyArea = tSourceImage.rect tPasteArea = [ \ point(tButtonWidth, tButtonHeight), \ point(0, tButtonHeight), \ point(0, 0), \ point(tButtonWidth, 0) \ ] tDownImage.copyPixels(tSourceImage, tPasteArea, tCopyArea) -- Move arrow itself down and to the right tCopyArea = tCopyArea + [1, 1, -2, -2] tPasteArea = tCopyArea + 1 tDownImage.copyPixels(tSourceImage, tPasteArea, tCopyArea) return tDownImage end mGetDownImage -- Cleaning up on mEraseTemporaryMembers(me) ---------------------------------------- -- SENT BY endSprite() -- ACTION: Erases the temporary text and bitmap members used by this -- behavior. -------------------------------------------------------------------- if ilk(pTempText, #member) then pTempText.erase() end if if ilk(pBitmap, #member) then pBitmap.erase() end if pSprite.member = pMember end mEraseTemporaryMembers -- EXTERNAL CALLS -- on mProvideData(me, aDataType, anID, aListOrInteger) ----------------- -- CALLED by GetReference() -- INPUT: could be #instance | #value -- may be a symbol or a list -- may be a list, an integer value or -- OUTPUT: Returns a pointer to this behavior instance or the value -- of this slider -------------------------------------------------------------------- case ilk(anID) of #symbol: -- use ID to filter for this behavior if anID <> #slider then exit end if #list, #propList: aListOrInteger = anID end case case aDataType of #instance: tData = me #value: tData = sliderValue otherwise: exit end case case ilk(aListOrInteger) of #list: aListOrInteger.append(tData) #propList: aListOrInteger.addProp(spriteNum, tData) otherwise: aListOrInteger = tData end case return aListOrInteger end mProvideData on mAcceptValue(me, anID, aNewValue) --------------------------------- -- CALLED by SetValue() -- INPUT: may be a symbol or a list -- should be an integer -- OUTPUT: Returns TRUE if the value is changed -------------------------------------------------------------------- case ilk(anID) of #symbol: -- use ID to filter for this behavior if anID <> #slider then exit end if #integer: aNewValue = anID otherwise: return FALSE end case if aNewValue < minValue or aNewValue > maxValue then return FALSE end if sliderValue = aNewValue me.mUpdateTextSprite() return TRUE end mAcceptValue -- UTILITY HANDLERS -- on mGetLocalizedString(me, aStringSymbol, asLinearList) --------------- -- CALLED by getPropertyDescriptionList() and mInitialize() -- INPUT: should be a symbol -- may be TRUE when this handler is called by -- getPropertyDescriptionList() -- OUTPUT: a localized string defined by -- -- NOTE: Storing all strings in one place makes it easy to -- localize the behavior. -- -- The getPropertyDescriptionList() handler may require a list of -- strings. These may need to be converted to a language-neutral -- #symbol when the behavior is used. In this case, this handler -- can return a property list where the properties are symbols and -- the values are localized strings. The getPD() handler can -- specify that this list be converted to a linear list for display, -- by setting to TRUE. The mInitialize() handler, -- called on beginSprite, can recuperate the full property list and -- use the following code to convert the appropriate string to a -- symbol: -- -- propertyList = me.mGetLocalizedString(#propList, FALSE) -- getPDLValue = propertyList.getOne(getPDLValue) -------------------------------------------------------------------- case aStringSymbol of #minValue: tString = "Minimum value for slider" #maxValue: tString = "Maximum value for slider" #sliderValue: tString = "Initial value for slider" #pixelSteps: tString = "Number of discrete pixel steps" #borderHeight: tString = "Border height in pixels" #revertDistance: tString = \ "Reinitialize value if distance to cursor is more than" #textSprite: tString = "Text/field member for slider value" #callHandler: tString = "Handler to call when a selection is made" #defaultHandler: tString = #valueSelected #callObject: tString = "This handler is located" #callObjects: tString = [ \ #movie: "in a Movie Script", \ #sprite: "in another behavior on this sprite", \ #sendAllSprites: "in behaviors on other sprites", \ #actorList: "in an instance on the actorList" \ ] otherwise: -- Unexpected value tString = string(aStringSymbol) end case if ilk(tString, #propList) then if asLinearList then -- Convert a property list to a linear list for getPDL() tTemp = tString tString = [] counter = tTemp.count repeat with i = 1 to counter tString[i] = tTemp[i] end repeat end if end if return tString end mGetLocalizedString -- BEHAVIOR DESCRIPTION AND PARAMETERS -- property ancestor -- Used at authortime for helper script on getPropertyDescriptionList(me) tPropertyList = [:] tCallObjects = me.mGetLocalizedString(#callObjects, TRUE) tPropertyList[ \ #minValue] = [ \ #comment: me.mGetLocalizedString(#minValue), \ #format: #integer, \ #default: 0 \ ] tPropertyList[ \ #maxValue] = [ \ #comment: me.mGetLocalizedString(#maxValue), \ #format: #integer, \ #default: 100 \ ] tPropertyList[ \ #sliderValue] = [ \ #comment: me.mGetLocalizedString(#sliderValue), \ #format: #string, \ #default: "0" \ ] tPropertyList[ \ #pixelSteps] = [ \ #comment: me.mGetLocalizedString(#pixelSteps), \ #format: #integer, \ #default: 100 \ ] tPropertyList[ \ #borderHeight] = [ \ #comment: me.mGetLocalizedString(#borderHeight), \ #format: #integer, \ #range: [#min: 7, #max: 16], \ #default: 8 \ ] tPropertyList[ \ #revertDistance] = [ \ #comment: me.mGetLocalizedString(#revertDistance), \ #format: #integer, \ #range: [#min: 16, #max: 160], \ #default: 64 \ ] tPropertyList[ \ #textSprite] = [ \ #comment: me.mGetLocalizedString(#textSprite), \ #format: #integer, \ #range: [#min: 0, #max: the lastChannel], \ #default: 1 \ ] tPropertyList[ \ #callHandler] = [ \ #comment: me.mGetLocalizedString(#callHandler), \ #format: #symbol, \ #default: me.mGetLocalizedString(#defaultHandler) \ ] tPropertyList[ \ #callObject] = [ \ #comment: me.mGetLocalizedString(#callObject), \ #format: #string, \ #range: tCallObjects, \ #default: tCallObjects[2] \ ] -- Optional section helperScript = "Find Sprite by Member Type (authorTime)" if the number of member(helperScript) > 0 then if member(helperScript).scriptText <> "" then ancestor = new(script helperScript) call(#smart, [me], tPropertyList, #textSprite, [#field, #text]) end if end if -- End of optional section return tPropertyList end getPropertyDescriptionList