Toggle Button...

Hi!
Just wanted to share a little hack I did on the “ui.lua” library to help dev have toggle button option…
Here’s my UI.lua (based on 1.3). My adds will appear between --ADDED TOGGLE IBOBX

  
-- ui.lua (currently includes Button class with labels, font selection and optional event model)  
  
-- Version 1.3 (fixes bug in label:setTextColor)  
  
module(..., package.seeall)  
  
-----------------  
-- Helper function for newButton utility function below  
local function newButtonHandler( self, event )  
  
 local result = true  
  
 local default = self[1]  
 local over = self[2]  
  
 --ADDED TOGGLE IBOBX  
 local checked = self[3]  
 --ADDED TOGGLE IBOBX  
  
 -- General "onEvent" function overrides onPress and onRelease, if present  
 local onEvent = self.\_onEvent  
  
 local onPress = self.\_onPress  
 local onRelease = self.\_onRelease  
  
 local buttonEvent = {}  
 if (self.\_id) then  
 buttonEvent.id = self.\_id  
 end  
  
  
 local phase = event.phase  
 if "began" == phase then  
 if over then   
 default.isVisible = false  
 over.isVisible = true  
 end  
  
 --ADDED TOGGLE IBOBX  
 if checked then  
 if self.\_ischecked==true then   
 checked.isVisible = false  
 end  
 end  
 --ADDED TOGGLE IBOBX  
  
 if onEvent then  
 buttonEvent.phase = "press"  
 result = onEvent( buttonEvent )  
 elseif onPress then  
 result = onPress( event )  
 end  
  
 -- Subsequent touch events will target button even if they are outside the stageBounds of button  
 display.getCurrentStage():setFocus( self )  
 self.isFocus = true  
  
 elseif self.isFocus then  
 local bounds = self.stageBounds  
 local x,y = event.x,event.y  
 local isWithinBounds =   
 bounds.xMin \<= x and bounds.xMax \>= x and bounds.yMin \<= y and bounds.yMax \>= y  
  
 if "moved" == phase then  
 if over then  
 -- The rollover image should only be visible while the finger is within button's stageBounds  
 default.isVisible = not isWithinBounds  
 over.isVisible = isWithinBounds  
 end  
 elseif "ended" == phase or "cancelled" == phase then   
 if over then   
 default.isVisible = true  
 over.isVisible = false  
 end  
  
 --ADDED TOGGLE IBOBX  
 if checked then  
 if self.\_ischecked==false then   
 default.isVisible = false  
 over.isVisible = false  
 checked.isVisible = true  
 self.\_ischecked=true  
 elseif self.\_ischecked==true then  
 default.isVisible = true  
 over.isVisible = false  
 checked.isVisible = false  
 self.\_ischecked=false  
 end  
 end  
 --ADDED TOGGLE IBOBX  
  
 if "ended" == phase then  
 -- Only consider this a "click" if the user lifts their finger inside button's stageBounds  
 if isWithinBounds then  
 if onEvent then  
 buttonEvent.phase = "release"  
 result = onEvent( buttonEvent )  
 elseif onRelease then  
 result = onRelease( event )  
 end  
 end  
 end  
  
  
 -- Allow touch events to be sent normally to the objects they "hit"  
 display.getCurrentStage():setFocus( nil )  
 self.isFocus = false  
 end  
 end  
  
 return result  
end  
  
---------------  
-- Button class  
  
function newButton( params )  
 local button, default, over, size, font, textColor, offset  
 if params.default then  
 button = display.newGroup()  
 default = display.newImage( params.default )  
 button:insert( default, true )  
 end  
  
 if params.over then  
 over = display.newImage( params.over )  
 over.isVisible = false  
 button:insert( over, true )  
 end  
  
 --ADDED TOGGLE IBOBX  
 local checked  
 if params.checked then  
 checked = display.newImage( params.checked )  
 checked.isVisible = false  
 button:insert( checked,true )  
 button.\_ischecked=false  
 end  
  
 --ADDED TOGGLE IBOBX  
  
 if params.text then  
 if ( params.size and type(params.size) == "number" ) then size=params.size else size=20 end  
 if ( params.font ) then font=params.font else font=native.systemFontBold end  
 if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end  
  
 -- Optional vertical correction for fonts with unusual baselines (I'm looking at you, Zapfino)  
 if ( params.offset and type(params.offset) == "number" ) then offset=params.offset else offset = 0 end  
  
 if ( params.emboss ) then  
 -- Make the label text look "embossed" (also adjusts effect for textColor brightness)  
 local textBrightness = ( textColor[1] + textColor[2] + textColor[3] ) / 3  
  
 local labelHighlight = display.newText( params.text, 0, 0, font, size )  
 if ( textBrightness \> 127) then  
 labelHighlight:setTextColor( 255, 255, 255, 20 )  
 else  
 labelHighlight:setTextColor( 255, 255, 255, 140 )  
 end  
 button:insert( labelHighlight, true )  
 labelHighlight.x = labelHighlight.x + 1.5; labelHighlight.y = labelHighlight.y + 1.5 + offset  
  
 local labelShadow = display.newText( params.text, 0, 0, font, size )  
 if ( textBrightness \> 127) then  
 labelShadow:setTextColor( 0, 0, 0, 128 )  
 else  
 labelShadow:setTextColor( 0, 0, 0, 20 )  
 end  
 button:insert( labelShadow, true )  
 labelShadow.x = labelShadow.x - 1; labelShadow.y = labelShadow.y - 1 + offset  
 end  
  
 local labelText = display.newText( params.text, 0, 0, font, size )  
 labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )  
 button:insert( labelText, true )  
 labelText.y = labelText.y + offset  
 end  
  
 if ( params.onPress and ( type(params.onPress) == "function" ) ) then  
 button.\_onPress = params.onPress  
 end  
 if ( params.onRelease and ( type(params.onRelease) == "function" ) ) then  
 button.\_onRelease = params.onRelease  
 end  
  
 if (params.onEvent and ( type(params.onEvent) == "function" ) ) then  
 button.\_onEvent = params.onEvent  
 end  
  
 --ADDED TOGGLE IBOBX  
 if params.checked then  
 button.\_ischecked = false  
 else  
 button.\_ischecked = nil  
 end  
 -- Public methods  
 function button:isChecked() -- return current state  
 return self.\_ischecked  
 end  
 --ADDED TOGGLE IBOBX  
  
 -- Set button as a table listener by setting a table method and adding the button as its own table listener for "touch" events  
 button.touch = newButtonHandler  
 button:addEventListener( "touch", button )  
  
 if params.x then  
 button.x = params.x  
 end  
  
 if params.y then  
 button.y = params.y  
 end  
  
 if params.id then  
 button.\_id = params.id  
 end  
  
  
  
 return button  
end  
--------------  
-- Label class  
  
function newLabel( params )  
 local labelText  
 local size, font, textColor, align  
 local t = display.newGroup()  
  
 if ( params.bounds ) then  
 local bounds = params.bounds  
 local left = bounds[1]  
 local top = bounds[2]  
 local width = bounds[3]  
 local height = bounds[4]  
  
 if ( params.size and type(params.size) == "number" ) then size=params.size else size=20 end  
 if ( params.font ) then font=params.font else font=native.systemFontBold end  
 if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end  
 if ( params.offset and type(params.offset) == "number" ) then offset=params.offset else offset = 0 end  
 if ( params.align ) then align = params.align else align = "center" end  
  
 if ( params.text ) then  
 labelText = display.newText( params.text, 0, 0, font, size )  
 labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )  
 t:insert( labelText )  
 -- TODO: handle no-initial-text case by creating a field with an empty string?  
  
 if ( align == "left" ) then  
 labelText.x = left + labelText.stageWidth \* 0.5  
 elseif ( align == "right" ) then  
 labelText.x = (left + width) - labelText.stageWidth \* 0.5  
 else  
 labelText.x = ((2 \* left) + width) \* 0.5  
 end  
 end  
  
 labelText.y = top + labelText.stageHeight \* 0.5  
  
 -- Public methods  
 function t:setText( newText )  
 if ( newText ) then  
 labelText.text = newText  
  
 if ( "left" == align ) then  
 labelText.x = left + labelText.stageWidth / 2  
 elseif ( "right" == align ) then  
 labelText.x = (left + width) - labelText.stageWidth / 2  
 else  
 labelText.x = ((2 \* left) + width) / 2  
 end  
 end  
 end  
  
 function t:setTextColor( r, g, b, a )  
 local newR = 255  
 local newG = 255  
 local newB = 255  
 local newA = 255  
  
 if ( r and type(r) == "number" ) then newR = r end  
 if ( g and type(g) == "number" ) then newG = g end  
 if ( b and type(b) == "number" ) then newB = b end  
 if ( a and type(a) == "number" ) then newA = a end  
  
 labelText:setTextColor( r, g, b, a )  
 end  
 end  
  
 -- Return instance (as display group)  
 return t  
  
end  
  

You can test it with this code:

local ui = require("ui")  
local testButton  
function btnOnPressHandler( event )  
 print(testButton:isChecked()) --this should return the button status  
end  
testButton = ui.newButton{  
 default = "btnMark.png",  
 over = "btnMark\_over.png",  
 checked = "btnMark\_checked.png",  
 onRelease = btnOnPressHandler,  
 emboss = true  
}  
testButton.x = display.contentWidth\*0.5  
testButton.y = display.contentHeight\*0.5  

You can get it with a little example here: http://www.ibobx.com/corona/ToggleButton.zip

Hope it’s useful…

Roberto.
[import]uid: 6046 topic_id: 1921 reply_id: 301921[/import]

Hi Roberto, I’m fairly new to Corona and I’m loving it. I used your hack on a project I’ve been working on quite a bit but I needed to be able to set the ‘checked’ state programmatically (in other words simulate a button click). To accomplish this I added the following function after your isChecked() function on line 196:

function button:setChecked( state ) -- set current state  
 if state then  
 checked.isVisible = true  
 default.isVisible = false  
 else  
 checked.isVisible = false  
 default.isVisible = true  
 end  
 button.\_ischecked = state  
end  

To call this:

myButton:setChecked( true )

Please let me know if there is something I left out or if there are other variables I need to set to finish the job. Like I said I’m new to this.

Thank you for the great work!

Carlos [import]uid: 6038 topic_id: 1921 reply_id: 23871[/import]