Difference between revisions of "ButtonMonster class"
From GiderosMobile
Line 2: | Line 2: | ||
This ButtonMonster class allows you to build any button, from the simplest (text) to the most complicated (tooltip, keyboard navigation). It is optimised for menus and in games. | This ButtonMonster class allows you to build any button, from the simplest (text) to the most complicated (tooltip, keyboard navigation). It is optimised for menus and in games. | ||
− | |||
− | |||
− | |||
− | |||
=== ButtonMonster '''Class''' === | === ButtonMonster '''Class''' === | ||
Line 14: | Line 10: | ||
-- Up, Down, Disabled, Hover, | -- Up, Down, Disabled, Hover, | ||
-- Sfx, Touch, Mouse and Keyboard navigation! | -- Sfx, Touch, Mouse and Keyboard navigation! | ||
− | v 0.2.0: 2023- | + | v 0.2.0: 2023-12-01 terminator, should be fine in games too now |
v 0.1.0: 2021-06-01 total recall, this class has become a Monster! best used in menus but who knows? | v 0.1.0: 2021-06-01 total recall, this class has become a Monster! best used in menus but who knows? | ||
v 0.0.1: 2020-03-28 init (based on the initial generic Gideros Button class) | v 0.0.1: 2020-03-28 init (based on the initial generic Gideros Button class) | ||
]] | ]] | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
-- Class | -- Class | ||
Line 88: | Line 47: | ||
self.params.pixelscalexdown = xparams.pixelscalexdown or self.params.pixelscalexup -- number | self.params.pixelscalexdown = xparams.pixelscalexdown or self.params.pixelscalexup -- number | ||
self.params.pixelscaleydown = xparams.pixelscaleydown or self.params.pixelscaleyup -- number | self.params.pixelscaleydown = xparams.pixelscaleydown or self.params.pixelscaleyup -- number | ||
− | self.params.pixelwidth = xparams.pixelwidth or | + | self.params.pixelwidth = xparams.pixelwidth or 24 -- 24, number (autoscale = x padding else width) |
self.params.pixelheight = xparams.pixelheight or self.params.pixelwidth -- number (autoscale = y padding else height) | self.params.pixelheight = xparams.pixelheight or self.params.pixelwidth -- number (autoscale = y padding else height) | ||
− | self.params.ninepatch = xparams.ninepatch or | + | self.params.ninepatch = xparams.ninepatch or 16 -- 0, 8, number |
-- text? | -- text? | ||
self.params.text = xparams.text or nil -- string | self.params.text = xparams.text or nil -- string | ||
Line 113: | Line 72: | ||
self.params.sound = xparams.sound or nil -- sound fx | self.params.sound = xparams.sound or nil -- sound fx | ||
self.params.volume = xparams.volume or nil -- sound volume | self.params.volume = xparams.volume or nil -- sound volume | ||
− | |||
− | |||
− | |||
-- let's go! | -- let's go! | ||
self:setButton() | self:setButton() | ||
Line 148: | Line 104: | ||
end | end | ||
-- first add pixel | -- first add pixel | ||
− | |||
if self.params.autoscale and self.params.text then | if self.params.autoscale and self.params.text then | ||
self.pixel = Pixel.new(self.params.pixelcolorup, self.params.pixelalphaup, | self.pixel = Pixel.new(self.params.pixelcolorup, self.params.pixelalphaup, | ||
Line 155: | Line 110: | ||
self.pixel = Pixel.new(self.params.pixelcolorup, self.params.pixelalphaup, | self.pixel = Pixel.new(self.params.pixelcolorup, self.params.pixelalphaup, | ||
self.params.pixelwidth, self.params.pixelheight) | self.params.pixelwidth, self.params.pixelheight) | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
self.pixel:setScale(self.params.pixelscalexup, self.params.pixelscaleyup) | self.pixel:setScale(self.params.pixelscalexup, self.params.pixelscaleyup) | ||
self.pixel:setAnchorPoint(0.5, 0.5) | self.pixel:setAnchorPoint(0.5, 0.5) | ||
− | if | + | self.pixel:setNinePatch(self.params.ninepatch) |
+ | if self.params.pixelimgup then self.pixel:setTexture(self.params.pixelimgup) | ||
+ | elseif self.params.pixelimgdown then self.pixel:setTexture(self.params.pixelimgdown) | ||
+ | elseif self.params.pixelimgdisabled then self.pixel:setTexture(self.params.pixelimgdisabled) | ||
+ | end | ||
self:addChild(self.pixel) | self:addChild(self.pixel) | ||
-- then add text? | -- then add text? | ||
Line 188: | Line 135: | ||
function ButtonMonster:updateVisualState() | function ButtonMonster:updateVisualState() | ||
local function visualState(btn, btnscalex, btnscaley, btnalpha, textcolor, textscalex, textscaley, | local function visualState(btn, btnscalex, btnscaley, btnalpha, textcolor, textscalex, textscaley, | ||
− | + | pixeltex, pixelcolor, pixelalpha, pixelscalex, pixelscaley) | |
btn:setScale(btnscalex, btnscaley) | btn:setScale(btnscalex, btnscaley) | ||
btn:setAlpha(btnalpha) | btn:setAlpha(btnalpha) | ||
Line 195: | Line 142: | ||
btn.text:setScale(textscalex, textscaley) | btn.text:setScale(textscalex, textscaley) | ||
end | end | ||
− | if | + | if pixeltex then btn.pixel:setTexture(pixeltex) end |
− | + | btn.pixel:setColor(pixelcolor, pixelalpha) | |
− | |||
− | |||
− | |||
− | |||
− | |||
btn.pixel:setScale(pixelscalex, pixelscaley) | btn.pixel:setScale(pixelscalex, pixelscaley) | ||
end | end | ||
Line 210: | Line 152: | ||
visualState(v, v.params.btnscalexdown, v.params.btnscaleydown, v.params.btnalphadown, | visualState(v, v.params.btnscalexdown, v.params.btnscaleydown, v.params.btnalphadown, | ||
v.params.textcolordisabled, v.params.textscalexdown, v.params.textscaleydown, | v.params.textcolordisabled, v.params.textscalexdown, v.params.textscaleydown, | ||
− | + | v.params.pixelimgdisabled, v.params.pixelcolordisabled, | |
+ | v.params.pixelalphadown, v.params.pixelscalexdown, v.params.pixelscaleydown) | ||
-- if v.ttiptext and not v.disabled then -- OPTION 1: hides tooltip when button is Disabled | -- if v.ttiptext and not v.disabled then -- OPTION 1: hides tooltip when button is Disabled | ||
if v.ttiptext then -- OPTION 2: shows tooltip even if button is Disabled, you choose! | if v.ttiptext then -- OPTION 2: shows tooltip even if button is Disabled, you choose! | ||
Line 221: | Line 164: | ||
visualState(v, v.params.btnscalexdown, v.params.btnscaleydown, v.params.btnalphadown, | visualState(v, v.params.btnscalexdown, v.params.btnscaleydown, v.params.btnalphadown, | ||
v.params.textcolordown, v.params.textscalexdown, v.params.textscaleydown, | v.params.textcolordown, v.params.textscalexdown, v.params.textscaleydown, | ||
− | + | v.params.pixelimgdown, v.params.pixelcolordown, | |
+ | v.params.pixelalphadown, v.params.pixelscalexdown, v.params.pixelscaleydown) | ||
if v.ttiptext then | if v.ttiptext then | ||
v.ttiptext:setText(v.params.tooltiptext) | v.ttiptext:setText(v.params.tooltiptext) | ||
Line 236: | Line 180: | ||
visualState(v, v.params.btnscalexup, v.params.btnscaleyup, v.params.btnalphaup, | visualState(v, v.params.btnscalexup, v.params.btnscaleyup, v.params.btnalphaup, | ||
v.params.textcolorup, v.params.textscalexup, v.params.textscaleyup, | v.params.textcolorup, v.params.textscalexup, v.params.textscaleyup, | ||
− | + | v.params.pixelimgup, v.params.pixelcolorup, | |
+ | v.params.pixelalphaup, v.params.pixelscalexup, v.params.pixelscaleyup) | ||
if v.ttiptext then v.ttiptext:setVisible(false) end | if v.ttiptext then v.ttiptext:setVisible(false) end | ||
end | end | ||
Line 245: | Line 190: | ||
visualState(self, self.params.btnscalexdown, self.params.btnscaleydown, self.params.btnalphadown, | visualState(self, self.params.btnscalexdown, self.params.btnscaleydown, self.params.btnalphadown, | ||
self.params.textcolordisabled, self.params.textscalexdown, self.params.textscaleydown, | self.params.textcolordisabled, self.params.textscalexdown, self.params.textscaleydown, | ||
− | + | self.params.pixelimgdisabled, self.params.pixelcolordisabled, | |
+ | self.params.pixelalphadown, self.params.pixelscalexdown, self.params.pixelscaleydown) | ||
-- if self.ttiptext and not self.disabled then -- OPTION 1: hides tooltip when button is Disabled | -- if self.ttiptext and not self.disabled then -- OPTION 1: hides tooltip when button is Disabled | ||
if self.ttiptext then -- OPTION 2: shows tooltip even if button is Disabled, you choose! | if self.ttiptext then -- OPTION 2: shows tooltip even if button is Disabled, you choose! | ||
Line 256: | Line 202: | ||
visualState(self, self.params.btnscalexdown, self.params.btnscaleydown, self.params.btnalphadown, | visualState(self, self.params.btnscalexdown, self.params.btnscaleydown, self.params.btnalphadown, | ||
self.params.textcolordown, self.params.textscalexdown, self.params.textscaleydown, | self.params.textcolordown, self.params.textscalexdown, self.params.textscaleydown, | ||
− | + | self.params.pixelimgdown, self.params.pixelcolordown, | |
+ | self.params.pixelalphadown, self.params.pixelscalexdown, self.params.pixelscaleydown) | ||
if self.ttiptext then | if self.ttiptext then | ||
self.ttiptext:setText(self.params.tooltiptext) | self.ttiptext:setText(self.params.tooltiptext) | ||
Line 264: | Line 211: | ||
visualState(self, self.params.btnscalexup, self.params.btnscaleyup, self.params.btnalphaup, | visualState(self, self.params.btnscalexup, self.params.btnscaleyup, self.params.btnalphaup, | ||
self.params.textcolorup, self.params.textscalexup, self.params.textscaleyup, | self.params.textcolorup, self.params.textscalexup, self.params.textscaleyup, | ||
− | + | self.params.pixelimgup, self.params.pixelcolorup, | |
+ | self.params.pixelalphaup, self.params.pixelscalexup, self.params.pixelscaleyup) | ||
if self.ttiptext then self.ttiptext:setVisible(false) end | if self.ttiptext then self.ttiptext:setVisible(false) end | ||
end | end | ||
Line 289: | Line 237: | ||
end | end | ||
self:updateVisualState() | self:updateVisualState() | ||
− | self:selectionSfx() -- play sound fx | + | self:selectionSfx() -- play sound fx? |
if self.ttiptext then -- set tooltip initial position | if self.ttiptext then -- set tooltip initial position | ||
if self.tooltiplayer then | if self.tooltiplayer then | ||
Line 369: | Line 317: | ||
timer:start() | timer:start() | ||
else | else | ||
+ | self.hover = false -- XXX | ||
self.onexit = true | self.onexit = true | ||
end | end | ||
Line 388: | Line 337: | ||
-- if button is on focus, stop propagation of touch events | -- if button is on focus, stop propagation of touch events | ||
function ButtonMonster:onTouchesBegin(ev) -- touch only | function ButtonMonster:onTouchesBegin(ev) -- touch only | ||
− | if self.focus then ev:stopPropagation() end | + | if self.focus then |
+ | ev:stopPropagation() | ||
+ | end | ||
end | end | ||
-- if button is on focus, stop propagation of touch events | -- if button is on focus, stop propagation of touch events | ||
function ButtonMonster:onTouchesMove(ev) -- touch only | function ButtonMonster:onTouchesMove(ev) -- touch only | ||
− | if self.focus then ev:stopPropagation() end | + | if self.focus then |
+ | ev:stopPropagation() | ||
+ | end | ||
end | end | ||
-- if button is on focus, stop propagation of touch events | -- if button is on focus, stop propagation of touch events | ||
Line 447: | Line 400: | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
-- ButtonMonster DEMO 1 | -- ButtonMonster DEMO 1 | ||
+ | -- bg | ||
application:setBackgroundColor(0x6c6c6c) | application:setBackgroundColor(0x6c6c6c) | ||
− | |||
-- font | -- font | ||
local myttf = TTFont.new("fonts/Cabin-Bold-TTF.ttf", 20) | local myttf = TTFont.new("fonts/Cabin-Bold-TTF.ttf", 20) | ||
− | |||
-- textures | -- textures | ||
local btnuptex = Texture.new("gfx/ui/btn_01_up.png") | local btnuptex = Texture.new("gfx/ui/btn_01_up.png") | ||
Line 462: | Line 414: | ||
local btn01 = ButtonMonster.new({ | local btn01 = ButtonMonster.new({ | ||
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | ||
+ | pixelscaleyup=0.7, pixelscaleydown=1, | ||
text="button 1", ttf=myttf, | text="button 1", ttf=myttf, | ||
sound=btnsound, volume=volume, | sound=btnsound, volume=volume, | ||
Line 467: | Line 420: | ||
local btn02 = ButtonMonster.new({ | local btn02 = ButtonMonster.new({ | ||
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | ||
+ | pixelscaleyup=0.7, pixelscaleydown=1, | ||
text="button 2", ttf=myttf, | text="button 2", ttf=myttf, | ||
sound=btnsound, volume=volume, | sound=btnsound, volume=volume, | ||
Line 472: | Line 426: | ||
local btn03 = ButtonMonster.new({ | local btn03 = ButtonMonster.new({ | ||
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | ||
+ | pixelscaleyup=0.7, pixelscaleydown=1, | ||
text="button 3", ttf=myttf, | text="button 3", ttf=myttf, | ||
sound=btnsound, volume=volume, | sound=btnsound, volume=volume, | ||
Line 477: | Line 432: | ||
local btn04 = ButtonMonster.new({ | local btn04 = ButtonMonster.new({ | ||
autoscale=false, | autoscale=false, | ||
− | pixelwidth=8* | + | pixelwidth=8*6, pixelheight=8*30, |
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | ||
+ | pixelscalexup=0.7, pixelscalexdown=1, | ||
pixelcolordown=0x00ff00, | pixelcolordown=0x00ff00, | ||
text="b\nt\nn\n \n4", ttf=myttf, | text="b\nt\nn\n \n4", ttf=myttf, | ||
Line 565: | Line 521: | ||
pixelwidth=8*5, pixelheight=8*24, | pixelwidth=8*5, pixelheight=8*24, | ||
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex, | ||
+ | ninepatch=32, | ||
pixelcolordown=0x00ff00, | pixelcolordown=0x00ff00, | ||
text="b\nt\nn\n \n4", ttf=myttf, | text="b\nt\nn\n \n4", ttf=myttf, |
Revision as of 18:43, 1 December 2023
This ButtonMonster class allows you to build any button, from the simplest (text) to the most complicated (tooltip, keyboard navigation). It is optimised for menus and in games.
ButtonMonster Class
--[[
-- ButtonMonster
-- Pixel, Image, 9patch, Text, Tooltip,
-- Up, Down, Disabled, Hover,
-- Sfx, Touch, Mouse and Keyboard navigation!
v 0.2.0: 2023-12-01 terminator, should be fine in games too now
v 0.1.0: 2021-06-01 total recall, this class has become a Monster! best used in menus but who knows?
v 0.0.1: 2020-03-28 init (based on the initial generic Gideros Button class)
]]
-- Class
ButtonMonster = Core.class(Sprite)
function ButtonMonster:init(xparams, xselector, xttlayer)
-- user params
self.params = xparams or {}
-- add keyboard navigation?
self.selector = xselector or nil -- button id selector
self.btns = nil -- assign this value directly from your code (you assign it a list of buttons)
-- add a layer for the tooltip?
self.tooltiplayer = xttlayer or nil
-- button params
self.params.autoscale = xparams.autoscale or (xparams.autoscale == nil) -- bool (default = true)
self.params.btnscalexup = xparams.btnscalexup or 1 -- number
self.params.btnscaleyup = xparams.btnscaleyup or self.params.btnscalexup -- number
self.params.btnscalexdown = xparams.btnscalexdown or self.params.btnscalexup -- number
self.params.btnscaleydown = xparams.btnscaleydown or self.params.btnscaleyup -- number
self.params.btnalphaup = xparams.btnalphaup or 1 -- number
self.params.btnalphadown = xparams.btnalphadown or self.params.btnalphaup -- number
-- pixel?
self.params.pixelcolorup = xparams.pixelcolorup or 0xffffff -- color
self.params.pixelcolordown = xparams.pixelcolordown or self.params.pixelcolorup -- color
self.params.pixelcolordisabled = xparams.pixelcolordisabled or 0x555555 -- color
self.params.pixelimgup = xparams.pixelimgup or nil -- img Up Texture
self.params.pixelimgdown = xparams.pixelimgdown or self.params.pixelimgup -- img Down Texture
self.params.pixelimgdisabled = xparams.pixelimgdisabled or self.params.pixelimgup -- img Disabled Texture
self.params.pixelalphaup = xparams.pixelalphaup or 1 -- number
self.params.pixelalphadown = xparams.pixelalphadown or self.params.pixelalphaup -- number
self.params.pixelscalexup = xparams.pixelscalexup or 1 -- number
self.params.pixelscaleyup = xparams.pixelscaleyup or self.params.pixelscalexup -- number
self.params.pixelscalexdown = xparams.pixelscalexdown or self.params.pixelscalexup -- number
self.params.pixelscaleydown = xparams.pixelscaleydown or self.params.pixelscaleyup -- number
self.params.pixelwidth = xparams.pixelwidth or 24 -- 24, number (autoscale = x padding else width)
self.params.pixelheight = xparams.pixelheight or self.params.pixelwidth -- number (autoscale = y padding else height)
self.params.ninepatch = xparams.ninepatch or 16 -- 0, 8, number
-- text?
self.params.text = xparams.text or nil -- string
self.params.ttf = xparams.ttf or nil -- ttf font
self.params.textcolorup = xparams.textcolorup or 0x0 -- color
self.params.textcolordown = xparams.textcolordown or self.params.textcolorup -- color
self.params.textcolordisabled = xparams.textcolordisabled or 0x777777 -- color
self.params.textalphaup = xparams.textalphaup or 1 -- number
self.params.textalphadown = xparams.textalphaup or self.params.textalphaup -- number
self.params.textscalexup = xparams.textscalexup or 1 -- number
self.params.textscaleyup = xparams.textscaleyup or self.params.textscalexup -- number
self.params.textscalexdown = xparams.textscalexdown or self.params.textscalexup -- number
self.params.textscaleydown = xparams.textscaleydown or self.params.textscaleyup -- number
-- tool tip?
self.params.tooltiptext = xparams.tooltiptext or nil -- string
self.params.tooltipttf = xparams.tooltipttf or nil -- ttf font
self.params.tooltiptextcolor = xparams.tooltiptextcolor or 0x0 -- color
self.params.tooltiptextscale = xparams.tooltiptextscale or 1 -- number
self.params.tooltipoffsetx = xparams.tooltipoffsetx or 0 -- number
self.params.tooltipoffsety = xparams.tooltipoffsety or 0 -- self.params.tooltipoffsetx -- number
-- audio?
self.params.sound = xparams.sound or nil -- sound fx
self.params.volume = xparams.volume or nil -- sound volume
-- let's go!
self:setButton()
-- update visual state
self.focus = false
self.hover = false
self.disabled = false
self:updateVisualState()
-- mouse event listeners
self:addEventListener(Event.MOUSE_DOWN, self.onMouseDown, self)
self:addEventListener(Event.MOUSE_MOVE, self.onMouseMove, self)
self:addEventListener(Event.MOUSE_UP, self.onMouseUp, self)
self:addEventListener(Event.MOUSE_HOVER, self.onMouseHover, self)
-- touches event listeners
self:addEventListener(Event.TOUCHES_BEGIN, self.onTouchesBegin, self)
self:addEventListener(Event.TOUCHES_MOVE, self.onTouchesMove, self)
self:addEventListener(Event.TOUCHES_END, self.onTouchesEnd, self)
self:addEventListener(Event.TOUCHES_CANCEL, self.onTouchesCancel, self)
end
-- FUNCTIONS
function ButtonMonster:setButton()
-- text dimensions
local textwidth, textheight
if self.params.text then
self.text = TextField.new(self.params.ttf, self.params.text, self.params.text)
self.text:setAnchorPoint(0.5, 0.5)
self.text:setScale(self.params.textscalexup, self.params.textscaleyup)
self.text:setTextColor(self.params.textcolorup)
self.text:setAlpha(self.params.textalphaup)
textwidth, textheight = self.text:getWidth(), self.text:getHeight()
end
-- first add pixel
if self.params.autoscale and self.params.text then
self.pixel = Pixel.new(self.params.pixelcolorup, self.params.pixelalphaup,
textwidth+self.params.pixelwidth, textheight+self.params.pixelheight)
else
self.pixel = Pixel.new(self.params.pixelcolorup, self.params.pixelalphaup,
self.params.pixelwidth, self.params.pixelheight)
end
self.pixel:setScale(self.params.pixelscalexup, self.params.pixelscaleyup)
self.pixel:setAnchorPoint(0.5, 0.5)
self.pixel:setNinePatch(self.params.ninepatch)
if self.params.pixelimgup then self.pixel:setTexture(self.params.pixelimgup)
elseif self.params.pixelimgdown then self.pixel:setTexture(self.params.pixelimgdown)
elseif self.params.pixelimgdisabled then self.pixel:setTexture(self.params.pixelimgdisabled)
end
self:addChild(self.pixel)
-- then add text?
if self.params.text then self:addChild(self.text) end
-- finally add tooltip?
if self.params.tooltiptext then
self.ttiptext = TextField.new(self.params.tooltipttf, self.params.tooltiptext, self.params.tooltiptext)
self.ttiptext:setScale(self.params.tooltiptextscale)
self.ttiptext:setTextColor(self.params.tooltiptextcolor)
self.ttiptext:setVisible(false)
if self.tooltiplayer then self.tooltiplayer:addChild(self.ttiptext)
else self:addChild(self.ttiptext)
end
end
end
function ButtonMonster:updateVisualState()
local function visualState(btn, btnscalex, btnscaley, btnalpha, textcolor, textscalex, textscaley,
pixeltex, pixelcolor, pixelalpha, pixelscalex, pixelscaley)
btn:setScale(btnscalex, btnscaley)
btn:setAlpha(btnalpha)
if btn.params.text then
btn.text:setTextColor(textcolor)
btn.text:setScale(textscalex, textscaley)
end
if pixeltex then btn.pixel:setTexture(pixeltex) end
btn.pixel:setColor(pixelcolor, pixelalpha)
btn.pixel:setScale(pixelscalex, pixelscaley)
end
if self.btns then
-- print("KEYBOARD NAVIGATION")
for k, v in ipairs(self.btns) do
if v.disabled then -- disabledState
visualState(v, v.params.btnscalexdown, v.params.btnscaleydown, v.params.btnalphadown,
v.params.textcolordisabled, v.params.textscalexdown, v.params.textscaleydown,
v.params.pixelimgdisabled, v.params.pixelcolordisabled,
v.params.pixelalphadown, v.params.pixelscalexdown, v.params.pixelscaleydown)
-- if v.ttiptext and not v.disabled then -- OPTION 1: hides tooltip when button is Disabled
if v.ttiptext then -- OPTION 2: shows tooltip even if button is Disabled, you choose!
v.ttiptext:setText("("..v.params.tooltiptext..")") -- extra!
if k == v.currselector then v.ttiptext:setVisible(true)
else v.ttiptext:setVisible(false)
end
end
elseif k == v.currselector then -- downState
visualState(v, v.params.btnscalexdown, v.params.btnscaleydown, v.params.btnalphadown,
v.params.textcolordown, v.params.textscalexdown, v.params.textscaleydown,
v.params.pixelimgdown, v.params.pixelcolordown,
v.params.pixelalphadown, v.params.pixelscalexdown, v.params.pixelscaleydown)
if v.ttiptext then
v.ttiptext:setText(v.params.tooltiptext)
if v.tooltiplayer then -- reset tooltip text position
v.ttiptext:setPosition(
v:getX()+v.params.tooltipoffsetx, v:getY()+v.params.tooltipoffsety)
else
v.ttiptext:setPosition(v:globalToLocal(
v:getX()+v.params.tooltipoffsetx, v:getY()+v.params.tooltipoffsety))
end
v.ttiptext:setVisible(true)
end
else -- upState
visualState(v, v.params.btnscalexup, v.params.btnscaleyup, v.params.btnalphaup,
v.params.textcolorup, v.params.textscalexup, v.params.textscaleyup,
v.params.pixelimgup, v.params.pixelcolorup,
v.params.pixelalphaup, v.params.pixelscalexup, v.params.pixelscaleyup)
if v.ttiptext then v.ttiptext:setVisible(false) end
end
end
else
-- print("TOUCH, MOUSE NAVIGATION")
if self.disabled then -- disabledState
visualState(self, self.params.btnscalexdown, self.params.btnscaleydown, self.params.btnalphadown,
self.params.textcolordisabled, self.params.textscalexdown, self.params.textscaleydown,
self.params.pixelimgdisabled, self.params.pixelcolordisabled,
self.params.pixelalphadown, self.params.pixelscalexdown, self.params.pixelscaleydown)
-- if self.ttiptext and not self.disabled then -- OPTION 1: hides tooltip when button is Disabled
if self.ttiptext then -- OPTION 2: shows tooltip even if button is Disabled, you choose!
self.ttiptext:setText("("..self.params.tooltiptext..")") -- extra!
if self.focus then self.ttiptext:setVisible(true)
else self.ttiptext:setVisible(false)
end
end
elseif self.focus or self.hover then -- downState
visualState(self, self.params.btnscalexdown, self.params.btnscaleydown, self.params.btnalphadown,
self.params.textcolordown, self.params.textscalexdown, self.params.textscaleydown,
self.params.pixelimgdown, self.params.pixelcolordown,
self.params.pixelalphadown, self.params.pixelscalexdown, self.params.pixelscaleydown)
if self.ttiptext then
self.ttiptext:setText(self.params.tooltiptext)
self.ttiptext:setVisible(true)
end
else -- upState
visualState(self, self.params.btnscalexup, self.params.btnscaleyup, self.params.btnalphaup,
self.params.textcolorup, self.params.textscalexup, self.params.textscaleyup,
self.params.pixelimgup, self.params.pixelcolorup,
self.params.pixelalphaup, self.params.pixelscalexup, self.params.pixelscaleyup)
if self.ttiptext then self.ttiptext:setVisible(false) end
end
end
end
-- DISABLED
function ButtonMonster:setDisabled(disabled)
if self.disabled == disabled then return end
self.disabled = disabled
self:updateVisualState()
end
function ButtonMonster:getDisabled()
return self.disabled
end
-- LISTENERS
-- MOUSE
function ButtonMonster:onMouseDown(ev) -- both mouse and touch
if self:hitTestPoint(ev.x, ev.y, true) then
self.focus = true
if self.btns then -- update keyboard button id selector
for k, v in ipairs(self.btns) do v.currselector = self.selector end
end
self:updateVisualState()
self:selectionSfx() -- play sound fx?
if self.ttiptext then -- set tooltip initial position
if self.tooltiplayer then
self.ttiptext:setPosition(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety)
else
self.ttiptext:setPosition(self:globalToLocal(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety))
end
end
ev:stopPropagation()
end
end
function ButtonMonster:onMouseMove(ev) -- both mouse and touch
if self.focus then
if self.ttiptext then -- tooltip follows position
if self.tooltiplayer then
self.ttiptext:setPosition(
ev.x + self.params.tooltipoffsetx, ev.y + self.params.tooltipoffsety)
else
self.ttiptext:setPosition(self:globalToLocal(
ev.x + self.params.tooltipoffsetx, ev.y + self.params.tooltipoffsety))
end
end
if not self:hitTestPoint(ev.x, ev.y, true) then
self.focus = false
self:updateVisualState()
end
ev:stopPropagation()
elseif self.ttiptext then -- reset tooltip text position
if self.tooltiplayer then
self.ttiptext:setPosition(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety)
else
self.ttiptext:setPosition(self:globalToLocal(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety))
end
end
end
function ButtonMonster:onMouseUp(ev) -- both mouse and touch
if self.focus then
self.focus = false
self:updateVisualState()
local e = Event.new("clicked")
e.currselector = self.selector -- update button id selector
e.disabled = self.disabled -- update button disabled
self:dispatchEvent(e) -- button is clicked, dispatch "clicked" event
ev:stopPropagation()
end
end
function ButtonMonster:onMouseHover(ev) -- mouse only
if self:hitTestPoint(ev.x, ev.y, true) then -- onenter
self.focus = true
self.hover = true
if self.ttiptext then -- tooltip follows mouse position
if self.tooltiplayer then
self.ttiptext:setPosition(
ev.x + self.params.tooltipoffsetx, ev.y + self.params.tooltipoffsety)
else
self.ttiptext:setPosition(self:globalToLocal(
ev.x + self.params.tooltipoffsetx, ev.y + self.params.tooltipoffsety))
end
end
-- execute onenter code only once
self.onenter = not self.onenter
if not self.onenter then self.moving = true end
if not self.moving then
if self.btns then -- update keyboard button id selector
for k, v in ipairs(self.btns) do v.currselector = self.selector end
end
local e = Event.new("hovered") -- dispatch "hovered" event
e.currselector = self.selector -- update button id selector
e.disabled = self.disabled -- update button disabled
self:dispatchEvent(e)
-- self:selectionSfx() -- play sound fx? you choose!
-- trick to remove residuals when fast moving mouse
local timer = Timer.new(100*1, 1) -- number of repetition, the higher the safer
timer:addEventListener(Event.TIMER, function() self:updateVisualState() end)
timer:start()
else
self.hover = false -- XXX
self.onexit = true
end
ev:stopPropagation()
else -- onexit
self.focus = false
self.hover = false
self.onenter = false
self.moving = false
if self.onexit then
-- execute onexit code only once
self.onexit = false
self:updateVisualState()
end
end
end
-- TOUCHES
-- if button is on focus, stop propagation of touch events
function ButtonMonster:onTouchesBegin(ev) -- touch only
if self.focus then
ev:stopPropagation()
end
end
-- if button is on focus, stop propagation of touch events
function ButtonMonster:onTouchesMove(ev) -- touch only
if self.focus then
ev:stopPropagation()
end
end
-- if button is on focus, stop propagation of touch events
function ButtonMonster:onTouchesEnd(ev) -- touch only
if self.focus then
ev:stopPropagation()
elseif self.ttiptext then -- reset tooltip text position
if self.tooltiplayer then
self.ttiptext:setPosition(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety)
else
self.ttiptext:setPosition(self:globalToLocal(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety))
end
end
end
-- if touches are cancelled, reset the state of the button
function ButtonMonster:onTouchesCancel(ev) -- app interrupted (phone call, ...), touch only
if self.focus then
self.focus = false
if self.btns then -- update keyboard button id selector
for k, v in ipairs(self.btns) do v.currselector = self.selector end
end
self:updateVisualState()
elseif self.ttiptext then -- reset tooltip text position
if self.tooltiplayer then
self.ttiptext:setPosition(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety)
else
self.ttiptext:setPosition(self:globalToLocal(
self:getX()+self.params.tooltipoffsetx, self:getY()+self.params.tooltipoffsety))
end
end
end
-- AUDIO
function ButtonMonster:selectionSfx()
if self.params.sound then
local snd = self.params.sound
local curr = os.timer()
local prev = snd.time
if curr - prev > snd.delay then
snd.sound:play():setVolume(self.params.volume)
snd.time = curr
end
end
end
ButtonMonster Demos
Demo 1
-- ButtonMonster DEMO 1
-- bg
application:setBackgroundColor(0x6c6c6c)
-- font
local myttf = TTFont.new("fonts/Cabin-Bold-TTF.ttf", 20)
-- textures
local btnuptex = Texture.new("gfx/ui/btn_01_up.png")
local btndowntex = Texture.new("gfx/ui/btn_01_down.png")
local btndisabledtex = Texture.new("gfx/ui/btn_01_disabled.png")
-- buttons sound
local btnsound = {sound=Sound.new("audio/Braam - Retro Pulse.wav"), time=0, delay=0.5} -- delay=0.5, 0.05
local volume = 0.3
-- buttons
local btn01 = ButtonMonster.new({
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
pixelscaleyup=0.7, pixelscaleydown=1,
text="button 1", ttf=myttf,
sound=btnsound, volume=volume,
}, 1)
local btn02 = ButtonMonster.new({
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
pixelscaleyup=0.7, pixelscaleydown=1,
text="button 2", ttf=myttf,
sound=btnsound, volume=volume,
}, 2)
local btn03 = ButtonMonster.new({
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
pixelscaleyup=0.7, pixelscaleydown=1,
text="button 3", ttf=myttf,
sound=btnsound, volume=volume,
}, 3)
local btn04 = ButtonMonster.new({
autoscale=false,
pixelwidth=8*6, pixelheight=8*30,
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
pixelscalexup=0.7, pixelscalexdown=1,
pixelcolordown=0x00ff00,
text="b\nt\nn\n \n4", ttf=myttf,
sound=btnsound, volume=volume,
}, 4)
local btnexit = ButtonMonster.new({
pixelcolorup=0xff0000, pixelcolordown=0x00ff00,
text="EXIT", ttf=myttf,
})
-- position
btn01:setPosition(16*5, 16*3)
btn02:setPosition(16*5, 16*8)
btn03:setPosition(16*5, 16*12.5)
btn04:setPosition(16*12, 16*8)
btnexit:setPosition(16*24, 16*16)
-- order
stage:addChild(btn01)
stage:addChild(btn02)
stage:addChild(btn03)
stage:addChild(btn04)
stage:addChild(btnexit)
-- add listeners
function clicked(btn)
print(btn.currselector, btn.disabled)
if btn.currselector == 2 then btn03:setDisabled(not btn03:getDisabled())
elseif btn.currselector == 5 then
if not application:isPlayerMode() then application:exit()
else print("EXIT")
end
end
end
btn01:addEventListener("clicked", clicked)
btn02:addEventListener("clicked", clicked)
btn03:addEventListener("clicked", clicked)
btn04:addEventListener("clicked", clicked)
btnexit:addEventListener("clicked", clicked)
Demo 2
-- ButtonMonster DEMO 2: keyboard navigation (arrow keys + ENTER)
application:setBackgroundColor(0x6c6c6c)
-- a gradient bg
local gradient = Pixel.new(0xffffff, 1, application:getContentWidth(), application:getContentHeight())
gradient:setColor(0x0, 1, 0xaa5500, 1, 15*16)
gradient:setAnchorPoint(0.5, 0.5)
-- font
local myttf = TTFont.new("fonts/Cabin-Bold-TTF.ttf", 20)
local myttipttf = TTFont.new("fonts/Cabin-Bold-TTF.ttf", 18)
-- textures
local btnuptex = Texture.new("gfx/ui/btn_01_up.png")
local btndowntex = Texture.new("gfx/ui/btn_01_down.png")
local btndisabledtex = Texture.new("gfx/ui/btn_01_disabled.png")
-- buttons tooltip layer
local tooltiplayer = Sprite.new()
-- buttons sound
local btnsound = {sound=Sound.new("audio/Braam - Retro Pulse.wav"), time=0, delay=0.5} -- delay=0.5, 0.05
local volume = 0.3
-- initial button selected
local selector = 1
-- buttons
local btn01 = ButtonMonster.new({
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
text="button 1", ttf=myttf,
tooltiptext="btn1", tooltipttf=myttipttf, tooltiptextcolor=0xaaff00, tooltipoffsetx=8*-1, tooltipoffsety=8*3,
sound=btnsound, volume=volume,
}, 1, tooltiplayer)
local btn02 = ButtonMonster.new({
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
text="button 2", ttf=myttf,
tooltiptext="click me!", tooltipttf=myttipttf, tooltiptextcolor=0xaaff00, tooltipoffsetx=8*-1, tooltipoffsety=8*3,
sound=btnsound, volume=volume,
}, 2, tooltiplayer)
local btn03 = ButtonMonster.new({
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
text="button 3", ttf=myttf,
tooltiptext="btn3", tooltipttf=myttipttf, tooltiptextcolor=0xaaff00, tooltipoffsetx=8*-1, tooltipoffsety=8*3,
sound=btnsound, volume=volume,
}, 3, tooltiplayer)
local btn04 = ButtonMonster.new({
autoscale=false,
pixelwidth=8*5, pixelheight=8*24,
pixelimgup=btnuptex, pixelimgdown=btndowntex, pixelimgdisabled=btndisabledtex,
ninepatch=32,
pixelcolordown=0x00ff00,
text="b\nt\nn\n \n4", ttf=myttf,
tooltiptext="btn4", tooltipttf=myttipttf, tooltiptextcolor=0xaaff00, tooltipoffsetx=8*-4, tooltipoffsety=8*12,
sound=btnsound, volume=volume,
}, 4, tooltiplayer)
local btnexit = ButtonMonster.new({
pixelcolorup=0xff0000, pixelcolordown=0x00ff00,
text="EXIT", ttf=myttf,
}, 5, tooltiplayer)
-- keyboard navigation
local btns = {}
btns[#btns + 1] = btn01
btns[#btns + 1] = btn02
btns[#btns + 1] = btn03
btns[#btns + 1] = btn04
btns[#btns + 1] = btnexit
-- position
gradient:setPosition(application:getContentWidth()/2, application:getContentHeight()/2)
btn01:setPosition(16*5, 16*3)
btn02:setPosition(16*5, 16*8)
btn03:setPosition(16*5, 16*12.5)
btn04:setPosition(16*12, 16*8)
btnexit:setPosition(16*24, 16*16)
-- order
stage:addChild(gradient)
for k, v in ipairs(btns) do
stage:addChild(v)
end
stage:addChild(tooltiplayer)
-- shared listener functions
function clicked(input, btn)
selector = btn.currselector
print(input, btn.currselector, btn.disabled)
if btn.currselector == 2 then btn03:setDisabled(not btn03:getDisabled())
elseif btn.currselector == 5 then
if not application:isPlayerMode() then application:exit()
else print("EXIT")
end
end
end
-- add listeners
for k, v in ipairs(btns) do
v:addEventListener("clicked", clicked, "mouse", v)
v:addEventListener("hovered", function(e) selector = e.currselector end)
v.btns = btns -- list of navigatable buttons
end
-- keyboard handler
function updateButton()
for k, v in ipairs(btns) do
v.currselector = selector
v:updateVisualState()
if k == selector then v:selectionSfx() end
end
end
stage:addEventListener(Event.KEY_DOWN, function(e)
if e.keyCode == KeyCode.UP or e.keyCode == KeyCode.LEFT then
selector -= 1 if selector < 1 then selector = #btns end updateButton()
elseif e.keyCode == KeyCode.DOWN or e.keyCode == KeyCode.RIGHT then
selector += 1 if selector > #btns then selector = 1 end updateButton()
elseif e.keyCode == KeyCode.ENTER then
clicked("keyboard", btns[selector])
end
end)
-- let's go!
updateButton() -- highlight first button