Create a calculator with Corona – P.1

In this multi-part tutorial, I’ll show you how to build a simple math calculator using the Corona SDK.

Part 1 – Creating the buttons

What you’ll need to begin:

  • The Corona SDK downloaded and installed.
  • Basic Lua language and Corona SDK knowledge. (go through the excellent tutorials at Ansca’s website).
  • A text editor.

First, we’ll create the calculator’s buttons. For a more modular and neater code structure I decided to write an external class that handles the creation of those buttons.
Although Lua doesn’t technically have classes, we can get a class-like functionality using tables and functions. You can find out more about this here, or simply follow my example below.

Create a new text file and name it “buttons.lua”. This will be our buttons class.

local class = {};
function class.newBtn()
	print("creating a new button");

	--our button display object
	local btn = display.newGroup();

	--Over here goes the code to draw the button

	--return the newly created button
	return btn;
end
return class;

Create another file and name it “main.lua”. This is the main program file. To test our button class, we will first require the external file, then create a temporary variable in main.lua to test it.
So, in “main.lua”, write:

--require the button constructor class
local btnClass = require "buttons";

--create a test button
local tempButton = btnClass.newBtn();

Now, run the code in the Corona Simulator.

The Corona Simulator should load, but the screen is blank! However, if you look at the terminal display window, you should see a message “creating a new button”. This is the message we printed within the newBtn() function.

To get the button showing up on the screen, we’ll have to create some graphical elements first. The button will consist of a rounded rectangle as the background and a text object as its label.
But, before we get there, lets set up a way to pass arguments that make the button customizable. This way we can set up different background colors, width and heights and, most importantly, different label texts when we create a new button.

In “buttons.lua”, change the line reading:

function class.newBtn()

to:

function class.newBtn(params)

For now, params will be a table that can hold the following button variables:

  • labelTxt – a string with the button’s label text
  • bgColor – background color.
  • txtColor – the label color.
  • width
  • height

Next, review the full newBtn() function with the comments I made in the text:

local class = {};
function class.newBtn(params)
--this function creates a new generic button
	--get parameters or set defaults
	local width = params.width or 50;
	local height = params.height or 35
	local labelTxt = params.labelTxt or "";
	local bgColor = params.bgColor or {100,100,200};
	local txtColor = params.txtColor or {255,255,255};

	--our button display object
	local btn = display.newGroup();

	--button background
	local bg = display.newRoundedRect(0,0,width,height,4);
	bg:setFillColor(bgColor[1],bgColor[2],bgColor[3]);

	--position the bg
	bg:setReferencePoint(display.CenterReferencePoint);
	bg.x , bg.y = 0,0;
	--insert the background to the button group:insert
	btn:insert(bg);

	--create the button's label
	local label = display.newText(labelTxt,0,0,native.systemFont, 18)
	label:setTextColor(txtColor[1],txtColor[2],txtColor[3]);

	--position the label
	label:setReferencePoint(display.CenterReferencePoint);
	label.x , label.y = 0 , 0;

	--insert the label into the button group
	btn:insert(label);
	btn:setReferencePoint(display.CenterReferencePoint);
	return btn;
end

Note how we define default values using the ‘or’ operator, if they are not specified in the params table.

You will now have to change the test button in “main.lua” to support the new params table.

Change “main.lua” to show the following code:

--require the button constructor class
local btnClass = require "buttons";
------------------------
--define text parameters
------------------------
--create the parameter table
local testParams = {};
--add parameters to the table
testParams.labelTxt = "5";

------------------------
--create a test button
------------------------
local tempButton = btnClass.newBtn(testParams)

------------------------
--position the test button at the middle of the screen
------------------------
tempButton.x , tempButton.y = display.contentWidth*.5 , display.contentHeight*.5;

Run the code in the simulator, you should see a little button with the label ‘5’ in the center of the screen.

Great! Now we just have to create the number buttons and the math function buttons. I wanted the different kinds of buttons to look slightly different from each other. This is a good opportunity to demonstrate how to implement simple inheritance behavior in Lua.

Specifically, we need to main groups of buttons for now: numerical buttons, and math buttons.

In “buttons.lua”, add the following functions just before the return class; line:

function class.newNumBtn(params)
	local numBtnParams = params or {};
	numBtnParams.bgColor = params.bgColor or {100,100,100};
	return class.newBtn(numBtnParams);
end

function class.newMathFuncBtn(params)
	local numBtnParams = params or {};
	numBtnParams.bgColor = params.bgColor or {150,50,50};
	return class.newBtn(numBtnParams);
end

The first function, newNumBtn(), creates a numerical button (which we will use for 0-9 and the decimal buttons), the second one creates a math function button (for “X,”-“,”+”,”=”, etc). Note that these functions simply define new parameters and than call our more generic newBtn() function, we created earlier.

Before we write the code to show all of the calculator’s buttons, lets make sure that our new button constructors work. Change “main.lua” to contin the following code:

--require the button constructor class
local btnClass = require "buttons";

------------------------
--create the test buttons
------------------------
local tempNumButton = btnClass.newNumBtn({labelTxt = "5"})

local tempMathButton = btnClass.newMathFuncBtn({labelTxt = "="})
------------------------
--position the test button at the middle of the screen
------------------------
tempNumButton.x , tempNumButton.y = display.contentWidth*.5 , display.contentHeight*.5;
tempMathButton.x , tempMathButton.y = display.contentWidth*.5, display.contentHeight*.5 + 80;

You can see how the background colors of each buttons are different. We could just as easily changed any other attribute of each button type.

Finally for this part of the tutorial, we will use the button class we created to construct the calculator’s button panel.

Here is the new, commented, code in “main.lua”:


-------------------------------------------
--the buttons panel
-------------------------------------------
--require the button constructor class
local btnClass = require "buttons";

--create the button panel
local buttonPanel = display.newGroup();
local bg = display.newRect(0,0,display.contentWidth-20,display.contentHeight - 100);
bg:setFillColor(50,50,50);
bg:setStrokeColor(255,255,255);
bg.strokeWidth = 1;
bg.x , bg.y = 0,0;
buttonPanel:insert(bg);

--position the button panel at the bottom center of the screen
buttonPanel:setReferencePoint(display.BottomCenterReferencePoint);
buttonPanel.x , buttonPanel.y = display.contentWidth * 0.5 , display.contentHeight - 2;

--array to hold all the buttons
local btns = {};
-------------------------------------------
--the number buttons
-------------------------------------------
--positioning variables
local padding = 5;
local rowSpacing = 50;
local colSpacing = 60;
local cols = 3; -- specify number of columns
local currentRow , currentCol = 0 , 4; --help us positioning the num buttons

for i=0 , 10 do
	local btnParams = {};
	if i==10 then
		btnParams.labelTxt = ".";
	else
		btnParams.labelTxt = i;
	end

	if i==0 then
	--make the '0' num button bigger then the rest of them
		btnParams.width = 110;
	end

	--create the button and put it in the array
	btns[#btns + 1] = btnClass.newNumBtn(btnParams);

	--we need to identify each button later, so we'll create a unique id for each one;
	if i==10 then
		--if '.' button
		btns[#btns].id = ".";
	else
		btns[#btns].id = i;
	end

	--position the buttons
	if i==0 then --'0' num button
		btns[#btns].x, btns[#btns].y = - 60 , 150;
	elseif i==10 then --decimal num button
		btns[#btns].x, btns[#btns].y = (cols-1) * colSpacing - 90 , 150;
	else --any other num button (1-9);
		btns[#btns].x, btns[#btns].y =  currentCol * colSpacing - 90, currentRow * rowSpacing + 150;
	end

	--insert the new button into the panel
	buttonPanel:insert(btns[#btns]);

	currentCol = currentCol + 1;
	if currentCol>= cols then currentCol = 0; end

	if (currentCol % 3 == 0) then currentRow = currentRow - 1; end
end

-------------------------------------------
--the math function buttons
-------------------------------------------

for i = 1, 7 do
	--create the button and put it in the array
	local btnParams = {};
	local id;
	local col , row , x , y;

	--configure button parameters and position
	if i==1 then
		btnParams.labelTxt = "/";
		id = "/";
		col , row = 2 , 4;
	elseif i==2 then
		btnParams.labelTxt = "X";
		id = "X";
		col , row = 3 , 4;
	elseif i==3 then
		btnParams.labelTxt = "+";
		id = "+";
		col , row = 3 , 2;
	elseif i==4 then
		btnParams.labelTxt = "-";
		id = "-";
		col , row = 3 , 3;
	elseif i==5 then
		btnParams.labelTxt = "C";
		id = "Clear";
		col , row = 0 , 4;
	elseif i==6 then
		btnParams.labelTxt = "+/-";
		id = "invertNegative";
		col , row = 1 , 4;
	else
		btnParams.labelTxt = "=";
		id = "=";
		btnParams.bgColor = {255,100,50};
		--the '=' button is twice the height of the other buttons
		btnParams.height = 85;
	end
	if id == "=" then
		x , y = 3 * colSpacing - 90, -rowSpacing + 175;
	else --any other math function button
		x , y = col * colSpacing - 90, -row * rowSpacing + 150;
	end
	--create the current math button
	btns[#btns + 1] = btnClass.newMathFuncBtn(btnParams);

	--insert the new button into the panel
	buttonPanel:insert(btns[#btns]);

	--position the button
	btns[#btns].x, btns[#btns].y  = x , y;

	--attach an id to the new button
	btns[#btns].id = id;

end

I inserted all the buttons into a table, that acts like an array, called ‘btns’. Each ‘btns’ member is a display group, and can hold its own variables or tables.
The ‘id’ member, added to each button in the btns table, will help us later identify what button the user tapped, when we code the calculator key events.
You should now have a completed calculator keypad on the screen. Unfortunately, these buttons still don’t do anything. That’s coming up in the next part of the tutorial.

Please let me know if you have any comments or questions!

Comments

  1. I admire your work , thanks for all the interesting articles .

  2. good….very fione code

  3. Very interesting points you have remarked, thanks for posting.

  4. HI. Great post
    Theres an issue with android. Check on the simulator, the buttons are not displayed with the correct size. I built it and tested it on a samsung galaxy sII, same issue.

    • Mauricio,
      Since I wanted to keep the tutorial simple, I didn’t add any extra files then those required for basic functionality.
      I believe the problem you are experiencing is because there is no config.lua file to specify the dimensions of the screen and how to handle other screen dimensions.
      To solve this, create a config.lua file in the project’s directory and set its ‘scale’ property to ‘letterbox’. You can find more info about this here:
      http://developer.coronalabs.com/content/configuring-projects

      • mauricio lopez says:

        Thanks a lot, that resolved the issued. I am newbie to corona and I did not know about the scale config parameter.

        What’s your opinion about the best arquitecture env inside corona. I come from the MVC world (symfony 2) and I am not really sure how to organize classes and responsabilities in LUA /corona

        I found this but it seems related to animations / scenes:

        https://github.com/Croisened/Corona-Shell-Project

        Whats your general opinion?

        • Glad that helped.
          I don’t think there are any official best practices on how to implement MVC with Lua (such as those provided by Apple for Obj-C and Cocoa programming). However, Lua is very flexible in the way it lets you structure your code. In this calculator tutorial I separated the functionality of the app into objects that could be classified as MVC (buttons and calcScreen are views, calcController is a controller and also contains the model, but you can easily separate them).
          If you want a stricter form of MVC you can separate your files into specific model, view, and controller files or directories. Each one can be a module that interacts with the other. Maybe it would be a good tutorial to write in the future :)

          • mauricio lopez says:

            Hi Amir.

            Thanks a lot for your insight. For sure, a post about an organization arquitecture would be amazing. I am following you on twitter now to be in touch when it happens.

            Btw I found this interesting idea:

            http://jessewarden.com/2011/08/why-robotlegs-for-games.html

            I am not familiar with actionscript but this guys were porting an mvc framework to corona/lua. The only problem I see is that the project in github seems not updated.

            There’s great potential in business apps that can run easily in android/IOS, but the corporate world demands conventions and best practices. I will continue my research. Thanks for your time.

  5. Excellent tutorial. Love your explanation. Please write more tutorials

Speak Your Mind

*