Skip to content

Instantly share code, notes, and snippets.

@nigjo
Last active January 31, 2021 14:44
Show Gist options
  • Save nigjo/da0c7e596eff6cd0703bd70880e6b0a7 to your computer and use it in GitHub Desktop.
Save nigjo/da0c7e596eff6cd0703bd70880e6b0a7 to your computer and use it in GitHub Desktop.
A simple attempt to create UI Buttons in Löve.

Löve2D UI Simple Buttons

A simple attempt to create UI Buttons in Löve.

Currently available:

  • (Quadratic) Buttons

  • Toggle Buttons

  • Checkboxes

  • Radiobuttons

see main.lua for sample usage

-- The MIT License (MIT)
--
-- Copyright © 2021 Jens Hofschröer
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the “Software”), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
utf8 = require("utf8")
local buttonCounter = 0
Button = {
basefont = love.graphics.getFont(),
useChars = false,
bgcol={0.5,0.5,.66},
hicol={0.55,0.55,.726},
locol={0.45,0.45,.594}
}
local function inShape(x,y,left,top,width,height)
return x>left and x<left+width and y>top and y<top+height
end
Button.drawAll = function(buttons)
for i=1,#buttons do
if buttons[i].visible ~= false then
buttons[i]:draw()
end
end
end
Button.findAction = function(x,y,buttons)
for i,button in ipairs(buttons) do
if button.enabled and inShape(x,y,unpack(button:getBounds())) then
return {
name="btn_"..button.name,
action=button.action
}
end
end
return {
name="none",
action=function() end
}
end
function Button:new(title, action, posx, posy, alignment)
local b={}
setmetatable(b, self)
self.__index = self
buttonCounter=buttonCounter+1
if title ~= nil then
local f = self.basefont
b.name = 'btn'..buttonCounter
b.title = love.graphics.newText(f, title)
b.enabled = true
b.btnAction = action or function() end
b.action = function()
b.btnAction(b)
end
b.posx = math.floor(posx or 0)
b.posy = math.floor(posy or 0)
b.width = b.title:getWidth()+16
b.height = b.title:getHeight()+8
b.tx = (b.width-b.title:getWidth())/2
b.ty = (b.height-b.title:getHeight())/2
Button.updateAlignment(b, alignment)
end
return b
end
function Button:getBounds()
return {
self.posx,self.posy,
self.width,self.height
}
end
function Button:setLocation(x,y,alignment)
self.posx = math.floor(x)
self.posy = math.floor(y)
self:updateAlignment(alignment)
end
function Button:updateAlignment(alignment)
if alignment~=nil then
if alignment=="BL" then
self.posy = self.posy - self.height
elseif alignment=="BR" then
self.posx = self.posx - self.width
self.posy = self.posy - self.height
elseif alignment=="TR" then
self.posx = self.posx - self.width
end
end
print("button @"..self.posx.."x"..self.posy.." with "..self.width.."x"..self.height)
end
function Button:checkAction(x,y)
if self.action ~= nil
and x>self.posx and x<self.width
and y>self.posy and y<self.height then
self.action(self)
end
end
Button.drawShadowText = function(text,x,y,enabled)
-- love.graphics.rectangle("line", self.title, self.posx, self.posy, self.width, self.height)
if enabled == true then
local r,g,b=love.graphics.getColor()
love.graphics.setColor(0,0,0)
love.graphics.draw(text, x+1, y+1)
love.graphics.setColor(r,g,b)
love.graphics.draw(text, x, y)
else
love.graphics.setColor(.66,.66,.66)
love.graphics.draw(text, x, y)
end
end
function Button:draw()
self:drawRect(false)
end
function Button:drawRect(pressed)
local px=0
local py=0
if pressed== true then
px=-1
py=-1
end
love.graphics.push()
love.graphics.setColor(self.bgcol)
love.graphics.rectangle("fill", self.posx, self.posy, self.width, self.height)
if pressed==true then love.graphics.setColor(self.locol) else love.graphics.setColor(self.hicol) end
love.graphics.line(self.posx, self.posy, self.posx+self.width, self.posy)
love.graphics.line(self.posx, self.posy, self.posx, self.posy+self.height)
if pressed==true then love.graphics.setColor(self.hicol) else love.graphics.setColor(self.locol) end
love.graphics.line(self.posx+self.width, self.posy+self.height, self.posx+self.width, self.posy)
love.graphics.line(self.posx+self.width, self.posy+self.height, self.posx, self.posy+self.height)
love.graphics.setColor(1,1,1)
Button.drawShadowText(self.title, px+self.posx+self.tx, py+self.posy+self.ty, self.enabled)
love.graphics.pop()
end
Selectable=Button:new()
function Selectable:new(title, action, posx, posy, alignment)
local btn = Button.new(self,title, function(context)
context:setChecked(not context.selected)
action(context)
end, posx, posy, alignment)
btn.selected = false
return btn
end
function Selectable:setChecked(checked)
self.selected = checked
print("changed to "..tostring(self.selected))
end
function Selectable:draw()
self:drawRect(self.selected)
end
Checkbox=Selectable:new()
function Checkbox:new(...)
local c = Selectable.new(self, ...)
if self.useChars then
c.checkedText = love.graphics.newText(Button.basefont, utf8.char(9746))
c.uncheckedText = love.graphics.newText(Button.basefont, utf8.char(9744))
c.width = c.width + c.uncheckedText:getWidth()+4
elseif c.title ~= nil then
c.width = c.width + c.title:getHeight()+4
end
return c
end
function Checkbox:draw()
love.graphics.push()
local dx
if self.useChars then
local t = self.uncheckedText
if self.selected then t = self.checkedText end
Button.drawShadowText(t, self.posx+self.tx, self.posy+self.ty, self.enabled)
dx = t:getWidth()
else
dx = self.title:getHeight()
love.graphics.rectangle("line", self.posx+self.tx,self.posy+self.ty,dx,dx)
if self.selected then
love.graphics.line(self.posx+self.tx,self.posy+self.ty,self.posx+self.tx+dx,self.posy+self.ty+dx)
love.graphics.line(self.posx+self.tx,self.posy+self.ty+dx,self.posx+self.tx+dx,self.posy+self.ty)
end
end
Button.drawShadowText(self.title, dx+4+self.posx+self.tx, self.posy+self.ty, self.enabled)
love.graphics.pop()
end
Radiobutton=Selectable:new()
function Radiobutton:new(...)
local c = Selectable.new(self, ...)
if self.useChars then
c.checkedText = love.graphics.newText(Button.basefont, utf8.char(9673))
c.uncheckedText = love.graphics.newText(Button.basefont, utf8.char(9675))
c.width = c.width + c.uncheckedText:getWidth()+4
elseif c.title ~= nil then
c.width = c.width + c.title:getHeight()+4
end
c.group = {}
return c
end
function Radiobutton:setGroup(group)
self.group = group
end
function Radiobutton:setChecked(checked)
print(tostring(self.title)..":"..tostring(checked).."/"..tostring(self.selected))
if checked==true and self.selected == false then
for i,btn in ipairs(self.group) do
if btn ~= self then
btn.selected = false
btn:setChecked(false)
end
end
self.selected = checked
end
end
function Radiobutton:draw()
love.graphics.push()
local dx
if self.useChars then
local t = self.uncheckedText
if self.selected then t = self.checkedText end
Button.drawShadowText(t, self.posx+self.tx, self.posy+self.ty, self.enabled)
dx=t:getWidth()
else
dx = self.title:getHeight()
love.graphics.circle("line", self.posx+self.tx+dx/2,self.posy+self.ty+dx/2,dx/2)
if self.selected then
love.graphics.circle("fill", self.posx+self.tx+dx/2,self.posy+self.ty+dx/2,dx/4)
end
end
Button.drawShadowText(self.title, dx+4+self.posx+self.tx, self.posy+self.ty, self.enabled)
love.graphics.pop()
end
-- The MIT License (MIT)
--
-- Copyright © 2021 Jens Hofschröer
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the “Software”), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
require "button"
function love.load()
dx,dy,width,height=love.window.getSafeArea()
Button.useChars = love.filesystem.getInfo("DejaVuSans.ttf", "file") ~= nil
if Button.useChars == true then
Button.basefont = love.graphics.newFont("DejaVuSans.ttf", 16)
end
local btnBorder = 8
local quit = Button:new("Quit", function(btn)
love.event.quit( "quit" )
end, width-btnBorder, height-btnBorder, "BR")
quit.enabled = false
quit.visible = false
local restart = Button:new("Restart", function(btn)
love.event.quit( "restart" )
end, btnBorder, btnBorder)
local toggle = Selectable:new("Toggle", function(btn)
if btn.selected == true then
btn.bgcol = {1,1,0,1}
else
btn.bgcol = {0,.8,0,1}
end
end, restart.posx+restart.width+btnBorder, restart.posy)
toggle.bgcol = {0,.8,0,1}
local checker = Checkbox:new("Checkböx", function(btn)
quit.enabled = btn.selected
quit.visible = btn.selected
end, restart.posx, restart.posy+restart.height+btnBorder)
local radio1 = Radiobutton:new("Option 1", function(btn)
end, btnBorder, height/2, "BL")
local radio2 = Radiobutton:new("Option 2", function(btn)
end, radio1.posx+radio1.width+btnBorder, radio1.posy)
local radios ={radio1,radio2}
radio1:setGroup(radios)
radio2:setGroup(radios)
buttons={
quit, restart,toggle,checker,radio1,radio2
}
end
function love.mousepressed(x, y, button, istouch, presses)
if button==1 then
mouseStartAction = Button.findAction(x-dx,y-dy,buttons)
end
end
function love.mousereleased( x, y, button, istouch, presses )
if button==1 then
mouseEndAction = Button.findAction(x-dx,y-dy,buttons)
if mouseEndAction.name == mouseStartAction.name then
print("perform action "..mouseEndAction.name)
mouseEndAction.action()
end
mouseEndAction = nil
end
mouseStartAction = nil
end
function love.draw()
love.graphics.translate(dx,dy)
for i,btn in ipairs(buttons) do
if btn.visible ~= false then btn:draw() end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment