GameShell Calculator App

I modified this Love2D Calculator https://github.com/imtn/love2d-calculator to work on Gameshell!
First create a new directory and two files named main.lua and conf.lua with the following code:

main.lua
--io.stdout:setvbuf("no") --enables live console output in sublime


--LOGIC ITEMS
local numOnHold = 0  -- After pressing +, -, *, /, =, previous number is kept in here.

local operatorJustSelected = false -- whether or not the most recent button press by a user, was on an operator
local justCalculated = false --Whether or not the user has just pressed enter or return to calculate.
local hasDecimal = false --Whether or not the calcDispStr number has a decimal point. If it does, we can't add any more decimal points
local maxCalcDispLen = 18 --If I didn't have this, the user could just keep typing numbers forever. This is the max, pleasant-to-see, digits that the calculator will display.
local currentButtonPressedIndex = 0 -- The index in buttonTable of whatever button is currently being held down. 0 means nothing is pressed right now because lua arrays start at 1

--FONTS
local buttonTextFont -- Font of all button text here
local calcDispStrFont-- Font of calcDispStr
local operatorButtonFont -- Font of + - * / =
local operatorDispFont -- Font of display in top right

--STRINGS & TEXT, CORRESPONDING
--You can set, but not get, the string displayed by LOVE2D's TEXT object. Therefore, we need a string and text for every item to display
local calcDispStr = "" -- string, to display on the face of the calculator, also needs to keep track of positive/negative as +/-
local calcDispText -- Text object to display numbers
local opDispStr = "" -- string to display
local opDispText -- Displays + - * / on the top right

local windowX, windowY = love.graphics.getDimensions() --width and height of window

--COLORS OF SHAPES AND TEXT
local backgroundColor = {64,64,64}
local rectangleColor = {41,41,41}
local buttonColor = {56,56,56}
local blackColor = {1,125,254}
local focusColor = {255,255,255}

--Basic arithmetic operations, to put in opTable and make the conditionals less wordy
local add = function(a, b) return a + b end
local subtract = function(a, b) return a - b end
local multiply = function(a, b)
	c =  a * b
	if c == 0 then
		c = 0
	end -- lua relies on the c compiler, which can produce negative 0, which is sure to confuse the user. this if statement gets rid of negative zero.
	return c
end
local divide = function(a, b) return a / b end

--List of lists. Each list is associated (conceptually, not actually codewise) with a button. Each list has six elements, which are in order: {x position, y position,  width, height, radius of the corners of the button's rounded rectangles, string to be printed on button}
--Draw these like love.graphics.rectangle("fill", windowX*v[1], windowY*v[2], windowX*v[3], windowY*v[4], windowX*v[5])
--Set the color as black for text, then love.graphics.print(" "..v[6], windowX*v[1], windowY*v[2])
--Where you are looping through everything in buttonTable and v is each inner table
local buttonTable = {
	{0.025, 0.634, 0.18, 0.17, 0, "1", 20, 5, 1, 3},
	{0.210, 0.634, 0.18, 0.17, 0, "2", 20, 5, 2, 3},
	{0.395, 0.634, 0.18, 0.17, 0, "3", 20, 5, 3, 3},
	{0.025, 0.457, 0.18, 0.17, 0, "4", 20, 5, 1, 2},
	{0.210, 0.457, 0.18, 0.17, 0, "5", 20, 5, 2, 2},
	{0.395, 0.457, 0.18, 0.17, 0, "6", 20, 5, 3, 2},
	{0.025, 0.280, 0.18, 0.17, 0, "7", 20, 5, 1, 1},
	{0.210, 0.280, 0.18, 0.17, 0, "8", 20, 5, 2, 1},
	{0.395, 0.280, 0.18, 0.17, 0, "9", 20, 5, 3, 1},
	{0.025, 0.813, 0.18, 0.17, 0, "(-)", 12, 2.5, 1, 4},
	{0.210, 0.813, 0.18, 0.17, 0, "0", 20, 5, 2, 4},
	{0.395, 0.813, 0.18, 0.17, 0, ".", 24, 2, 3, 4},
	{0.620, 0.280, 0.177, 0.232, 0, "/", 23, 7, 4, 1},
	{0.803, 0.280, 0.177, 0.232, 0, "*", 20, 15, 5, 1},
	{0.620, 0.520, 0.177, 0.232, 0, "+", 15, 7, 4, 2},
	{0.803, 0.520, 0.177, 0.232, 0, "-", 23, 7, 5, 2},
	{0.620, 0.760, 0.177, 0.225, 0, "c", 17, 7, 4, 3}, --clear, delete, whatever you want to call it 
	{0.803, 0.760, 0.177, 0.225, 0, "=", 17, 7, 5, 3}
}

local currFocus = {x=2,y=1}

--CALLBACK
function love.load()
	love.graphics.setBackgroundColor(backgroundColor)
	buttonTextFont = love.graphics.newFont(30)
	calcDispStrFont = love.graphics.newFont(21)
	operatorButtonFont = love.graphics.newFont(36)
	operatorDispFont = love.graphics.newFont(17)
	calcDispText = love.graphics.newText(calcDispStrFont, calcDispStr)
	opDispText = love.graphics.newText(operatorDispFont, opDispStr)
end	

--CALLBACK
function love.resize(w, h)
  windowX = w
  windowY = h
end

--CALLBACK
function love.draw()
	--Border edge offset of 0.01

	-- Draws the number buttons area
	love.graphics.setColor(rectangleColor)
	love.graphics.rectangle("fill", windowX * 0.02, windowY * 0.27, windowX * 0.56, windowY * 0.72, windowX * 0)

	-- Draws the operator buttons area
	love.graphics.setColor(rectangleColor)
	love.graphics.rectangle("fill", windowX * 0.61, windowY * 0.27, windowX * 0.38, windowY * 0.72, windowX * 0)

	--draws the actual buttons
	for _, v in ipairs(buttonTable) do
		love.graphics.setColor(buttonColor)
		love.graphics.rectangle("fill", windowX*v[1], windowY*v[2], windowX*v[3], windowY*v[4], windowX*v[5])
		love.graphics.setFont(buttonTextFont)
		love.graphics.setColor(blackColor)
		love.graphics.print(tostring(v[6]), windowX*v[1]+v[7], windowY*v[2]+v[8])

		if currFocus.x == v[9] and currFocus.y == v[10] then -- Button is focused
			love.graphics.setColor(focusColor)
			love.graphics.rectangle("line", windowX*v[1], windowY*v[2], windowX*v[3], windowY*v[4], windowX*v[5])
		end
	end

	-- Draws the number display area
	love.graphics.setColor(rectangleColor)
	love.graphics.rectangle("fill", windowX * 0.01, windowY * 0.01, windowX * 0.98, windowY * 0.25, windowX * 0.01)

	-- create love.graphics.newText and displays calcDispStr, and another for the + - * /
	love.graphics.setColor(blackColor)
	love.graphics.draw(calcDispText, windowX * 0.98 - calcDispText:getWidth(), windowY * 0.24 - calcDispText:getHeight())
	love.graphics.draw(opDispText, windowX * 0.98 - opDispText:getWidth(), windowY * 0.12 - opDispText:getHeight())
end

--All key pressing AND button pressing gets relegated to this function
function love.keypressed( key )
	if key == "escape" then
		love.event.quit()
	end
	
	if key == "j" then -- A Button
		for _,btn in pairs(buttonTable) do
			if btn[9] == currFocus.x and btn[10] == currFocus.y then
				buttonPressed(btn[6])
			end
		end
	end
	
	if key == "left" then
		moveFocus(-1, 0)
	end
	
	if key == "right" then
		moveFocus(1, 0)
	end
	
	if key == "up" then
		moveFocus(0, -1)
	end
	
	if key == "down" then
		moveFocus(0, 1)
	end
end

function buttonPressed(key)
	if  key ~= "(-)" then
		handleNumberAndOperatorInputs(key)
	elseif calcDispStr ~= "" then
		if calcDispStr:sub(1, 1) == "-" then
			calcDispStr = calcDispStr:sub(2)
		else
			calcDispStr = "-"..calcDispStr
		end
		calcDispText:set(calcDispStr)
	end
end

function moveFocus(xDir, yDir)
	if currFocus.y == 3 and currFocus.x == 3 and xDir == 1 then -- movement from btn "3" to "+" when going right
		currFocus.y = 2
	end
	
	if currFocus.y == 3 and currFocus.x == 4 and xDir == -1 then -- movement from btn "c" to "." when going left
		currFocus.y = 4
	end
	
	currFocus.x = currFocus.x + xDir
	currFocus.y = currFocus.y + yDir
	
	if currFocus.x <= 0 then
		currFocus.x = 1
	end
	
	if currFocus.x >= 6 then
		currFocus.x = 5
	end
	
	if currFocus.y <= 0 then
		currFocus.y = 1
	end
	
	if currFocus.y >= 5 and currFocus.x < 4 then
		currFocus.y = 4
	end
	
	if currFocus.y >= 4 and currFocus.x > 3 then
		currFocus.y = 3
	end
end

function handleNumberAndOperatorInputs(key)
	--handles numpad and home keyboard inputs
	local numTable = {["0"]=0,["1"]=1,["2"]=2,["3"]=3,["4"]=4,["5"]=5,["6"]=6,["7"]=7,["8"]=8,["9"]=9, ["."]=".", kp0=0,kp1=1,kp2=2,kp3=3,kp4=4,kp5=5,kp6=6,kp7=7,kp8=8,kp9=9,["kp."]="."}

	local opTable = {["+"]=add,["-"]=subtract,["*"]=multiply,["/"]=divide,["kp+"]=add,["kp-"]=subtract,["kp*"]=multiply,["kp/"]=divide}

	--timesIAppreciatedSwitchStatements++;

	--KEY IS DELETE
	if key == "backspace" or key == "delete" or key == "c" then
		--clear everything
		calcDispStr = ""
		calcDispText:set(calcDispStr)
		opDispStr = ""
		opDispText:set(opDispStr)
		operatorJustSelected = false
		numOnHold = 0
		hasDecimal = false
		justCalculated = false

	-- KEY IS CALCULATE
	elseif key == "return" or key == "kpenter" or key == "=" then
		--calculate anything if operator is selected
		--if no selection, display what is currently displaying
		if opDispStr == "" then
			--nothing
		else
			--some operation happened, loop through possible operations
			--but if operator has just been selected, don't do anything
			if not operatorJustSelected then
				local tempNum = opTable[opDispStr](numOnHold, tonumber(calcDispStr))
				numOnHold, calcDispStr = tempNum, tostring(tempNum)
				opDispStr = ""
				opDispText:set(opDispStr)
						calcDispText:set(calcDispStr)
				hasDecimal = false
				justCalculated = true
			end
		end

	--KEY IS OPERATE
	elseif opTable[key] ~= nil then
		if key:len() > 1 then
		  key = key:sub(3)
		end

		--If just selected operator, replace that operator
		--If we haven't just selected the operator and the operator display string ain't empty,
			--perform opDispStr with numOnHold and calcDispStr, put in numOnHold and calcDispStr
			--and set opDispStr as key, set operatorJustSelected to true
		--If we haven't just selected the operator and opDispStr is empty, opDispText
		  -- set operatorJustSelected to true
		  -- set opDispStr as key
		if operatorJustSelected then
			opDispStr = key
			opDispText:set(opDispStr)
		elseif opDispStr ~= "" then
			operatorJustSelected = true
			local tempNum = opTable[opDispStr](numOnHold, tonumber(calcDispStr))
			numOnHold, calcDispStr = tempNum, tostring(tempNum)
			calcDispText:set(calcDispStr)
			opDispStr = key
			hasDecimal = false
		else --opDispStr == ""
			numOnHold = tonumber(calcDispStr)
			operatorJustSelected = true
			hasDecimal = false
			opDispStr = key
			opDispText:set(opDispStr)
		end

	--KEY IS NUMBER
	elseif numTable[key] ~= nil and not ((numTable[key] == ".") and hasDecimal) then
		if numTable[key] == "." then
			hasDecimal = true
		end
  
		--if just selected operator, add replace calcDispStr with new number, but the old calcDispStr should still be in numOnHold
		if operatorJustSelected or justCalculated then
			calcDispStr = tostring(numTable[key])
			operatorJustSelected = false
			justCalculated = false
		elseif calcDispStr:len() < maxCalcDispLen then
			calcDispStr = calcDispStr .. numTable[key]
		end
    
		--Below if statement prevents arithmetic involving only a decimal point
		if calcDispStr == "." then
		  calcDispStr = "0."
		end

		calcDispText:set(calcDispStr)
	end
end
conf.lua
--Configuration File
function love.conf(t)
	t.title = "Calculator"
	t.window.width = 320
	t.window.height = 240
	
	t.modules.physics = false
	t.modules.sound = false
	t.modules.mouse = false
	t.modules.audio = false
	t.modules.filesystem = false
	t.modules.image = false
	t.modules.touch = false
end

Then, create a new file named Calculator.sh in /home/cpi/apps/launcher/Menu/GameShell/ with the following content:

love path/to/the/folder

where path/to/the/folder leads to the directory with the two files you just created.

Now reboot and you’re done!
Have fun!

5 Likes

Here is also a little icon I made. Just upload to /home/cpi/apps/launcher/skin/default/Menu/GameShell/ and rename to Calculator.png
Calculator

1 Like

it’s very useful
mark it
:slight_smile:

1 Like

And I made an example ,put all your stuff together

it’s much simple as in this way creating apps

put code ,script , icon together and then just

git clone it into ~/apps/launcher/Menu/GameShell , restart launcher, then it’ll be there

Hope you like it

PS:
seems love2d 11.1 will have issues with this codes, every button became almost white ,I dont know why

1 Like

The buttons will be white because love.graphics.setColour now takes values between 0-1 for the rgba values, in previous versions it took values between 0-255.

So basically, everything that was not black in a previous version, will now be white.

A simple fix is to change:

love.graphics.setColor(r, g, b, a)

to:

love.graphics.setColor(r / 255, g / 255, b / 255, a / 255)

Hope that helps!

2 Likes

thanks !

but in GS
love is 0.10.2
so there is no problem for now

1 Like

Wow thank you for creating a repo! I wanted to aswell but I decided to just paste the code here…
Also I didn’t know that you can have both, Bash Script and Icon in ~/apps/launcher/skin/default/Menu/GameShell/! Didn’t try yet!

well

It’s a new feature since 1.21 of launcher ,if I remember correctly

glad to hear that you like it

1 Like

Would anyone mind putting a screenshot up of the app? Thanks

Screenshot

1 Like

nice, thank you. If only the screen was a touchscreen …