Create a calculator with Corona P.3

Make sure to visit the first two parts of this tutorial.
Part 1
Part 2

Its time to add some math logic, or a “calculator brain” to the app.
Create a new file in the same folder and name it ‘calcController.lua’. This class controls all the math functions of the calculator, such as add, divide, subtract, multiply, or any more complicated functions that you’d like to add later on your on.

Basic operating principle

Our calculator stores up to two numbers, called operands, entered by the user, and performs a math operation on them when a math button or the equal buttons are pressed. The result of the operation is stored in operand1.

Here’s an example: The user inputs 56.4 + 100. Operand1 is 56.4, operand2 is 100. The result of the operation, 156.4, is stored as the new value of operand1 and displayed on the calculator’s readout screen. Next the user hits “X” and 10, the number “10” is inserted into operand2 and a multiply operation is performed between the two operands, resulting in 1564, which is now the new value of operand1.

Here is our full ‘calcController.lua’ class:

local class = {}

local operand1, operand2 = 0, 0;
local operator = "+";

--this flag will be 'true' if the user tries to divide by 0
class.foundError = false;

--"setter" functions
function class.setOp1(value)
	operand1 = value;
end

function class.setOp2(value)
	operand2 = value;
end

function class.setOperator(str)
	--this function sets the operator ('+','-','/','*')
	if str ~= "=" then
		operator = str;
	end
end

--"getter" functions
function class.getOp1()
	return operand1;
end

function class.getOp2()
	return operand2;
end

function class.getOperator()
	return operator;
end

--math functions
function class.add()
	class.setOp1(operand1 + operand2);
end

function class.sub()
	class.setOp1(operand1 - operand2);
end

function class.mul()
	class.setOp1(operand1 * operand2);
end

function class.div()
	local result
	if tonumber(operand2) ~= 0 then
		result = operand1 / operand2;
	else
		result = 0;
		class.foundError = true;
	end
	class.setOp1(result);
end

function class.reset()
	class.setOp1(0);
	class.setOp2(0);
	class.setOperator("+");
	class.foundError = false;
end

function class.performOperation()
	local mathOp = class.getOperator();
	
	if mathOp == "-" then
		class.sub();
	elseif mathOp == "+" then
		class.add();
	elseif mathOp == "X" then
		class.mul();
	elseif mathOp == "/" then
		class.div();
	end

end
return class;

In order to use the class in our app, you need to require it in “main.lua”:

-------------------------------------------
--require the calculator controller class
------------------------------------------
local cc = require "calcController";

For number input, we have to differentiate between two modes. The first is when a user starts entering a new number. This happens after the clear button or a math function button was pressed. When this occurs, the calculator clears the screen, adds the digit to the readout string and then displays it. If the last key pressed (stored in var ‘lastInput’) was a digit or the decimal key, that means that the user is currently typing a number. Therefore every digit pressed at this mode would be added to the right side of the readout string.

Lets get to work. At the beginning of ‘main.lua’ add the following variables and flags:

-------------------------------------------
--Variables
-------------------------------------------
--our readout string
local displayStr = "0";

--limit the number of digits that can fit in the display
local maxDigits = 19;

--Type of last button pressed. 
--accepts "none", "equal", "num" or "math"
local lastInput = "none"; 

-------------------------------------------
--flags
-------------------------------------------
--should start capturing a new number
--when digit or decimal pressed?
local startNewNumber = true;

--was the last button pressed "-"?
local negPressedLast = false;

--was the decimal button tapped?
local decimalPressed = false;

In order to put everything together, you’ll have to add some code to the the button tap listener functions we created in the previous part of the tutorial.
Here are the modified event listeners:

local function invertNegBtnTapped(event)
--handles the +/- event
	local num = tonumber(displayStr);
	--if num is not nil and is smaller then 0
	if num and num<0 then
		--remove the "-" (first char of the string)
		displayStr = string.sub(displayStr,2);
	--if num is not nil and is larger then 0
	elseif num and num>0 then
		--add a "-"
		displayStr = "-"..displayStr;
	end
	--show the modified num on screen
	if num then calcScr.setTxt(displayStr); end
	return true;
end

local function mathBtnTapped(event)
--handles the math buttons ("/","*","+,"-" etc)
	local targetID = event.target.id;
	
	if targetID == "-" and startNewNumber then
		negPressedLast = true;
	else
		negPressedLast = false;
	end
	
	
	--if this is not the first time in a row the '='
	--button was pressed, dont change op2
	--Example: the user enters "3+5 = = =" result is 18
	if lastInput ~= "equal" then
		cc.setOp2(tonumber(displayStr));
	end
	
	if targetID ~= "=" and lastInput=="equal" then
		--treat the last result as it was a num entered by user
		cc.setOp1(tonumber(displayStr));
		cc.setOp2(0);
	else  
		cc.performOperation();
	end
	
	
	displayStr = cc.getOp1();
	
    if targetID == "=" then
    	lastInput = "equal"
    else
    	lastInput = "math"
    end
    
    --set the typed math function (X, /, -, +) as the last operator
    cc.setOperator(targetID);
    
    --check for errors found by the calculator controller
    --such as a divide by zero
    if cc.foundError then displayStr = "ERROR"; end
    
    --deal with the clear button
    if targetID =="Clear" then
		clear();
	end
	
    --show operand1 on the readout screen
    calcScr.setTxt(displayStr);
	return true
end

local function numBtnTapped(event)
--handles the number buttons ('0'-'9')
	local targetID = event.target.id;
	print("You pressed "..targetID);
	
	--reset the readout string, if this is a new digit sequence to enter
	--if lastinput was "equal" we are starting a completely new
	--sequence of calculations, so we call clear()
	if lastInput == "equal" then
		clear();
	elseif lastInput ~= "num" then 
		displayStr = "0"; 
		decimalPressed = false; 
	end
	
	--don't allow display longer then 'maxDigits'
	if (string.len(displayStr) < maxDigits) then
		displayStr = displayStr .. targetID;
	end
	
	--check if the "-" button was pressed before this number
	if negPressedLast then
		displayStr = "-"..displayStr;
		negPressedLast = false;
	end
	
	--flag that the last button was a number button
	lastInput = "num";
	
	--clean '0' on left side of the readout string
	displayStr = cleanLeftZeros(displayStr);
	
	calcScr.setTxt(displayStr);
	startNewNumber = false;
	return true
end

local function decimalBtnTapped(event)
--handles the decimal button ('.')
	local targetID = event.target.id;
	print("You pressed "..targetID);
	
	--reset the readout string, if this is a new digit sequence to enter
	--if lastinput was "equal" we are starting a completely new
	--sequence of calculations, so we call clear()
	if lastInput == "equal" then
		clear();
	elseif lastInput ~= "num" then 
		displayStr = "0"; 
		decimalPressed = false; 
	end
	
	if not decimalPressed and (string.len(displayStr) < maxDigits) then
		displayStr = displayStr..".";
		decimalPressed = true;
		lastInput = "num";
	end
	calcScr.setTxt(displayStr);
	return true;
end

Note that I added a separate event listener for the ‘invertNegative’ button. This listener wasn’t introduced in the previous part for simplicity.

These are the highlights of the code. You can get the entire project here:
SimpleCalc.zip

Let me know if you have any comments, questions, or suggestions for improvement.

Comments

  1. Lucas P says:

    Would it be okay if I used some of your code in a class project / app? I will make sure to mention where I got the code from.

Speak Your Mind

*