Unit Converter with nice menu style

This time i wrote a code for unit conversion. For menu options i used the idea of “Sounds Plus” Example of codea but i enhanced it. Demo video and source code can be found in http://www.knowhowto.com.au/coding-of-unit-converter-using-codea

:slight_smile:

This is great, @rashedlatif! I can’t believe I missed it the first time around.

It seems that we have one more utility program. You might send your code to Codea Competition #3 @Zoyt.

Thanks Simeon and Sanit. i tried my best. next time i will do even better i hope :slight_smile: @sanit i might send it to competition but dont know how to :frowning:

Thanks! Entry #2. I still need a few more… Thanks!

@Zoyt - This is for your competition.

Is the link mentionned above unavailable ?

If so is it possible to share the code another time for the Codea community ?
Thanks in advance unless your code is still in competition as mentionned above.

Sorry the link isn’t available now. Here is the code.

Main



-- Use this function to perform your initial setup
function setup()
    areaUnits = {"Square Miles", "Square Yard", "Square Feet", "Square Inches", "Square Kilometers", "Hactares", "Acres", "Square Meters", "Square Centimeter", "Square Millimeters"}
    lengthUnits = {"Miles", "Naut. Miles", "Fathoms", "Yard", "Feet", "Inches", "Kilometers", "Meters", "Centimeters", "Millimeters", "Smoot"}
    temperatureUnits = {"Fahrenheit", "Celcius", "Kelvin"}
    speedUnits = {"Knot", "Miles/hr", "Miles/min", "Feet/min", "Feet/sec", "Kilometers/hr", "Kilometers/min", "Meters/sec"}
    volumeUnits = {"Cubic Feet", "Gallon(imp.)", "Gallon(US)", "Quart(US)", "Pint(US)", "Fl oz(US)", "Cup", "Tablespoon", "Tea Spoon", "Dram(US)", "Cubic Meters", "Litre"}
    massUnits = {"Short Ton", "Pound", "Ounce", "Troy Ounce", "Stone", "Long Ton", "Metric Ton", "Kilogram", "Grams"}
    
    frmtextField = TextField("From")
    totextField = TextField("To")

    categoryMenu = CategoryMenu("Category", 25, 25)
    areaMenu = SubMenu(areaUnits,"Area", 15, 25)
    lengthMenu = SubMenu(lengthUnits,"Length", 15, 22)
    speedMenu = SubMenu(speedUnits,"Speed", 15, 25)
    temperatureMenu = SubMenu(temperatureUnits, "Temperature", 15, 25)
    massMenu = SubMenu(massUnits, "Mass", 15, 25)
    volumeMenu = SubMenu(volumeUnits, "Volume", 15, 20)
    fill(170, 79, 23, 255)
    
    closeButton = Buttons("X",170,79,23,255,255,255,255,255,22,20)
    
    keyPad = KeyPad()
    keyFlag = false
    currentMenu = categoryMenu
    
    frmValue="0"
    toValue=""
    frmUnit = ""
    toUnit = ""
    frmUnitcounter = 0
    toUnitcounter = 0
    dotCounter = 0
    signCounter = 0
    displayMode(FULLSCREEN)
    screenMsg = "Select one unit from each side for conversion"
    xp=WIDTH
    alp = 0 
end


-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    --background(255, 255, 255, 255)

    -- This sets the line thickness
    strokeWidth(5)
    
    -- Do your drawing here
    
    currentMenu:draw()
    if(xp>=WIDTH - closeButton.size.x/2 - 10) then
        xp=xp-3
    elseif (xp<WIDTH - closeButton.size.x/2 - 13) then
        xp = WIDTH
    end

    --closeButton.pos = vec2(WIDTH - closeButton.size.x/2 - 10, closeButton.size.y)
    closeButton.pos = vec2(xp,closeButton.size.y)
    closeButton:draw()
end

function touched(touch)
    if currentMenu == categoryMenu then
        CategoryMenu:touched(touch)
    elseif currentMenu == areaMenu then
        areaMenu:touched(touch)
    elseif currentMenu == lengthMenu then
        lengthMenu:touched(touch)
    elseif currentMenu == temperatureMenu then
        temperatureMenu:touched(touch)
    elseif currentMenu == speedMenu then
        speedMenu:touched(touch)
    elseif currentMenu == volumeMenu then
        volumeMenu:touched(touch)
    elseif currentMenu == massMenu then
        massMenu:touched(touch)
    end
    
    if keyFlag == true then
        keyPad:touched(touch)
    end
    closeButton:touched(touch)
end


KeyPad


KeyPad = class()

function KeyPad:init(x)
    -- you can accept and set parameters here
    self.keypadButtons = {}
    self.pos = vec2(0,0)
    self.keys = {"1","2","3","4","5","6","7","8","9",".","0","-","Back","Hide"}
    for _, v in pairs (self.keys) do
        --fill(44, 51, 61, 255)
        table.insert(self.keypadButtons,Buttons(v,44,51,61,255,255,255,255,255,20,40))     
    end
end

function KeyPad:draw()
    -- Codea does not automatically call this method
    
    fill(61, 71, 81, alp)
    rectMode(CENTER)
    rect(self.pos.x,self.pos.y,290,460)

    l = self.pos.x-90
    t = self.pos.y+100
    if alp <180 then
        alp = alp + 10
    end
    if alp >= 170 then
    x = 1
    for _, v in pairs (self.keypadButtons) do 
        v.pos = vec2(l,t)
        v:draw()

        if x == 3 then
            t = t - 70
            l = self.pos.x-90
            x=0
        else
            l = l+90
        end
        x=x+1
            
    end
    end
end
    

function KeyPad:touched(touch)
    -- Codea does not automatically call this method
        for _,b in pairs(self.keypadButtons) do
            b:touched(touch, 0)
        end
    
end

SubMenu


SubMenu = class()

function SubMenu:init(units,title,fntsize,btnHeight)
    -- you can accept and set parameters here
    self.title = title
    self.fromButtons = {}
    self.toButtons = {}
    self.units = units
    self.buttonFontsize = fntsize
    self.btnHeight = btnHeight
    for _, v in pairs (self.units) do
        fill(58, 65, 112, 255)
        r,g,b,a = fill()
        fill(255, 255, 255, 255)
        fr,fg,fb,fa = fill()
        table.insert(self.fromButtons, Buttons(v,r,g,b,a,fr,fg,fb,fa,self.buttonFontsize,self.btnHeight))
        fill(73, 99, 157, 255)
        r,g,b,a = fill()
        table.insert(self.toButtons, Buttons(v,r,g,b,a,fr,fg,fb,fa,self.buttonFontsize,self.btnHeight))
    end

    fill(38, 43, 49, 255)
    r,g,b,a = fill()
    self.convertButton = Buttons("Convert",r,g,b,a,fr,fg,fb,fa,self.buttonFontsize,self.btnHeight)
    fill(108, 143, 28, 255)
    r,g,b,a = fill()
    self.backButton = Buttons("Main Menu",r,g,b,a,fr,fg,fb,fa,self.buttonFontsize,self.btnHeight)
    
end

function SubMenu:onEnter()
    clearParameters()
    clearOutput()
    self:setTodefault()  
end

-- This function sets the default color of button and confirms no button is selected when entering
-- a menu screen
function SubMenu:setTodefault()  
    
    for _, v in pairs (self.fromButtons) do
        v.selectFlag = false
        v.color = color(58,65,112,255)
        fill(255, 255, 255, 255)
        v.fr,v.fg,v.fb,v.fa = fill()
    end
    
    for _, v in pairs (self.toButtons) do
        v.selectFlag = false
        v.color = color(73,99,157,255)
        fill(255, 255, 255, 255)
        v.fr,v.fg,v.fb,v.fa = fill()
    end
    frmValue = "0"
    toValue = ""
    frmUnit = ""
    toUnit = ""
    frmUnitcounter = 0
    toUnitcounter = 0
    keyFlag = false
    xp = WIDTH
    alp = 0
end

function SubMenu:draw()
    -- Codea does not automatically call this method
    pushStyle()
    self:drawInterface()
    if keyFlag == true then
        
        keyPad.pos = vec2((WIDTH + totextField.pos.x+totextField.size.x/2)/2, HEIGHT-HEIGHT*0.14-225)  
        keyPad:draw()
    end
    popStyle()
end

function SubMenu:drawInterface()
    
    background(200, 200, 200, 255)
    local top = HEIGHT - (HEIGHT*0.08)
    local left = WIDTH/6
    fill(0, 0, 0, 255)
    font("HelveticaNeue-CondensedBold")
    fontSize(20)
    text("From", left,top)
    left = WIDTH/2.25
    text("To", left,top)
    
    top = HEIGHT - (HEIGHT*0.14)
    left = WIDTH/6
    
    --Drawing first set of buttons - "From" buttons
    for _,b in pairs(self.fromButtons) do
        b.pos = vec2(left,top)
        b:draw()
        top = top - b.size.y - 5
    end
    
    
    -- Drawing the first text field which will accept input
    frmtextField.pos = vec2(left,top-15)
    frmtextField:draw()
    
    top = HEIGHT - (HEIGHT*0.14)
    left = WIDTH/2.25
    
    --Drawing second set of buttons - "To" Buttons
    for _,b in pairs(self.toButtons) do
        b.pos = vec2(left,top)
        b:draw()
        top = top - b.size.y - 5
    end
    
    
    --Drawing second text field which will display the result
    totextField.pos = vec2(left,top-15)
    totextField:draw()
    
    fill(42, 49, 65, 255)
    text("=", (frmtextField.pos.x+totextField.pos.x)/2,totextField.pos.y)
    
    fill(60, 60, 60, 146)
    font("Arial-BoldMT")
    fontSize(self.buttonFontsize)
    text(frmUnit, frmtextField.pos.x, frmtextField.pos.y+30)
    text(toUnit, totextField.pos.x, frmtextField.pos.y+30)
    --Drawing the button which will be used to trigger conversion function
    self.convertButton.pos = vec2((frmtextField.pos.x+totextField.pos.x)/2,frmtextField.pos.y-frmtextField.size.y-10)
    self.convertButton:draw()
    
    --Drawing the button which is used to go back to main category menu
    self.backButton.pos = vec2(WIDTH - self.backButton.size.x/2 - 80, self.backButton.size.y)
    self.backButton:draw()
  
    top = top + 25
    left = totextField.pos.x+totextField.size.x/2+10
    local h = totextField.size.y*#self.units + 5*(#self.units-1)
    local w = WIDTH - left -10
    textMode(CENTER)
    font("HelveticaNeue-CondensedBold")
    if currentMenu == temperatureMenu then
        fontSize(35)
    else
        fontSize(50)
    end
    if keyFlag == false then
        fill(76, 119, 125, 210)
        roundRect(left,top, w,h,20)
        fill(196, 193, 193, 210)
        roundRect(left+10, top+10, w-20, h-20,20)   
        top = top+h/2
    else
        top = top+h-72
    end
    
    fill(122, 127, 124, 255)
    text(self.title,left+w/2+2,top-2)
    fill(32, 85, 52, 255)
    text(self.title,left+w/2,top)
     
    rectMode(CORNER)
    strokeWidth(0)
    fill(74, 83, 87, 95)
    rect(WIDTH-WIDTH*0.98,HEIGHT-HEIGHT*0.055,WIDTH-35,HEIGHT-40)
    font("HelveticaNeue-CondensedBold")
    fontSize(20)
    fill(255, 255, 255, 123)
    text(screenMsg, WIDTH/2+1, HEIGHT-(HEIGHT*0.032)-1) 
    if (string.len(screenMsg)>45) then
        fill(255, 255, 255, 255)
    else
        fill(69, 80, 95, 255)
    end
    text(screenMsg, WIDTH/2, HEIGHT-(HEIGHT*0.032)) 
end

function SubMenu:touched(touch)
    -- Codea does not automatically call this method
        for _,b in pairs(self.fromButtons) do
            b:touched(touch, 1)
        end
        for _,b in pairs(self.toButtons) do
            b:touched(touch, 2)
        end
        self.convertButton:touched(touch,0)
        frmtextField:touched(touch,0)
        self.backButton:touched(touch,0)
end
function Conversion (title)
    -- Conversion table for Area
    local areaTable ={{1,3097600,27878400,4014490000,2.589988,258.9988,640, 2589988, 25899880000,2589988000000},
                     {0.00000032283,1,9,1296,0.000000836127,0.00008361274,0.0002066116,0.8361274,8361.274,836127.4},
                     {0.00000003587,0.1111111,1,144,0.000000092903,0.000009290304,0.00002295684,0.09290304,929.0304,92903.04},
                    {0.000000000249,0.0007716049,0.006944444,1,0.000000000645,0.000000064516,0.000000159422,0.00064516,6.4516,645.16},
                    {0.3861022,1195990,10763910,1550003000,1,100,247.1054,1000000,10000000000,1000000000000},
                    {0.003861022,11959.9,107639.1,15500030,0.01,1,2.471054,10000,100000000,10000000000},
                    {0.0015625,4840,43560,6272640,0.004046856,0.4046856,1,4046.856,40468560,4046856000},
                    {0.000000386102,1.19599,10.76391,1550.003,0.000001,0.0001,0.0002471054,1,10000,1000000},
                    {0.000000000038,0.000119599,0.001076391,0.1550003,0.0000000001,0.00000001,0.00000002471,0.0001,1,100},
                    {0.00000000000038,0.00000119599,0.00001076391,0.00155003,0.000000000001,0.0000000001,0.000000000247,0.000001,0.01,1}}
            
    -- Conversion Table for Length                
    local lengthTable = {{1,0.8689762,880,1760,5280,63360,1.609344,1609.344,160934.4,1609344,945.6716},
{1.150779,1,1012.686,2025.372,6076.115,72913.39,1.852,1852,185200,1852000,1088.259},
{0.001136364,0.000987473,1,2,6,72,0.0018288,1.8288,182.88,1828.8,1.074627},
{0.0005681818,0.0004937365,0.5,1,3,36,0.0009144,0.9144,91.44,914.4,0.5373134},
{0.0001893939,0.0001645788,0.1666667,0.3333333,1,12,0.0003048,0.3048,30.48,304.8,0.1791045},
{0.00001578283,0.0000137149,0.01388889,0.02777778,0.08333333,1,0.0000254,0.0254,2.54,25.4,0.01492537},
{0.6213712,0.5399568,546.8066,1093.613,3280.84,39370.08,1,1000,100000,1000000,587.6131},
{0.0006213712,0.0005399568,0.5468066,1.93613,3.28084,39.37008,0.001,1,100,1000,0.5876131},
{0.000006213712,0.000005399568,0.005468066,0.01093613,0.0328084,0.3937008,0.00001,0.01,1,10,0.005876131},
{0.000000621371,0.000000539956,0.0005468066,0.001093613,0.00328084,0.03937008,0.000001,0.001,0.1,1,0.0005876131},
{0.001057449,0.0009188985,0.9305556,1.861111,5.583333,67,0.0017018,1.7018,170.18,1701.8,1}}

    -- Conversion table for Temperature

    local temperatureTable = {{frmValue,5/9*(frmValue-32),5/9*(frmValue-32)+273.15},
                              {(frmValue*9/5)+32,frmValue,frmValue+273.15},
                              {(frmValue-273.15)*9/5+32,frmValue-273.15,frmValue}}

    -- Conversion table for Mass
    local massTable = {{1,2000,32000,29166.67,142.8571,0.8928571,0.9071847,907.1847,907184.7},
                       {0.0005,1,16,14.58333,0.07142857,0.0004464286,0.0004535924,0.4535924,453.5924},
                      {0.00003125,0.0625,1,0.9114583,0.004464286,0.00002790179,0.00002834952,0.02834952,28.34952},
                    {0.00003428571,0.06857143,1.097143,1,0.004897959,0.00003061224,0.00003110348,0.03110348,31.10348},
                    {0.007,14,224,204.1667,1,0.00625,0.006350293,6.350293,6350.293},
                    {1.12,2240,35,840,32666.67,160,1,1.016047,1016.047,1016047},
                    {1.102311,2204.623,35273.96,32150.75,157.473,0.9842065,1,1000,1000000},
                    {0.001102311,2.204623,35.27396,32.15075,0.157473,0.0009842065,0.001,1,1000},
                    {0.000001102311,0.002204623,0.03527396,0.03215075,0.000157473,0.000000984206,0.000001,0.001,1}}
   
     --Conversion Table for Speed
    local speedTable = {{1,1.150779,0.01917966,101.2686,1.68781,1.852,0.03086667,0.5144444},
    {0.8689762,1,0.01666667,88,1.466667,1.609344,0.0268224,0.44704},
    {52.13857,60,1,5280,88,96.56064,1.609344,26.8224},
    {0.00987473,0.01136364,0.0001893939,1,0.01666667,0.018288,0.0003048,0.00508},
    {0.5924838,0.6818182,0.01136364,60,1,1.09728,0.018288,0.3048},
    {0.5399568,0.6213712,0.01035619,54.68066,0.9113444,1,0.01666667,0.2777778},
    {32.39741,37.28227,0.6213712,3280.84,54.68067,60,1,16.66667},
    {1.943844,2.236936,0.03728227,196.8504,3.28084,3.6,0.06,1}}
    
    local volumeTable = {{1,6.228836,7.48052,29.92208,59.84416,957.5065,119.6883,1915.013,5745.039,7660.052,0.02831685,28.31685},
    {0.1605437,1,1.20095,4.8038,9.607599,153.7216,19.2152,307.4432,922.3296,1229.773,0.00454609,4.54609},
    {0.1336806,0.8326742,1,4,8,128,16,256,768,1024,0.003785412,3.785412},
    {0.03342014,0.2081685,0.25,1,2,32,4,64,192,256,0.000946353,0.946353},
    {0.01671007,0.1040843,0.125,0.5,1,16,2,32,96,128,0.0004731765,0.4731765},
    {0.001044379,0.006505267,0.0078125,0.03125,0.0625,1,0.125,2,6,8,0.00002957353,0.02957353},
    {0.008355035,0.05204214,0.0625,0.25,0.5,8,1,16,48,64,0.0002365882,0.2365882},
    {0.0005221897,0.003252634,0.00390625,0.015625,0.03125,0.5,0.0625,1,3,4,0.00001478676,0.01478676},
    {0.0001740632,0.001084211,0.001302083,0.005208333,0.01041667,0.1666667,0.02083333,0.3333333,1,1.333333,0.000004928922,0.004928922},
    {0.0001305474,0.0008131584,0.0009765625,0.00390625,0.0078125,0.125,0.015625,0.25,0.75,1,0.000003696691,0.003696691},
    {35.31467,219.9692,264.172,1056.688,2113.376,33814.02,4226.753,67628.04,202884.1,270512.2,1,1000},
    {0.03531467,0.2199692,0.264172,1.056688,2.113376,33.81402,4.226753,67.62804,202.8841,270.5122,0.001,1}}
    
    
                    
    local i
    local j
    local counter = 1
    
    for _, v in pairs (currentMenu.fromButtons) do
        if v.title == frmUnit then
            i = counter
        end
        if v.title == toUnit then
            j = counter
        end
        counter = counter + 1
    end
     if title == "Area" then
        return frmValue*areaTable[i][j]
     elseif title == "Length" then
        return frmValue*lengthTable[i][j]
     elseif title == "Temperature" then
        return temperatureTable[i][j]
     elseif title == "Mass" then
        return frmValue*massTable[i][j]
     elseif title == "Speed" then
        return frmValue*speedTable[i][j]
     elseif title == "Volume" then
        return frmValue*volumeTable[i][j]
     end      
end

Buttons


Buttons = class()

function Buttons:init(title,r,g,b,a,fr,fg,fb,fa,fnt,h)
    -- you can accept and set parameters here
    self.title = title
    self.pos = vec2(0,0)
    self.size = vec2(0,0)
    self.fntSize = fnt
    self.height = h
    self.action = function() self:buttonAction(self.title) end
    -- These variables are used to give flexibility for changing button color and font color 
    -- in different menu screen and also to handle color change when a button is selected
    self.r = r
    self.g = g
    self.b = b
    self.a = a
    
    self.fr = fr
    self.fg = fg
    self.fb = fb
    self.fa = fa
    
    tr = fr tg = fg tb = fb ta = fa
    self.color = color(self.r,self.g,self.b,self.a)
    
    self.selectFlag = false
end

function Buttons:hit(p)
    local l = self.pos.x - self.size.x/2
    local r = self.pos.x + self.size.x/2
    local t = self.pos.y + self.size.y/2
    local b = self.pos.y - self.size.y/2
    if p.x > l and p.x < r and p.y > b and p.y < t then
        return true
    else
        return false
    end
end

function Buttons:draw()
    -- Codea does not automatically call this method
    pushStyle()
    fill(self.color)
    font("ArialRoundedMTBold")
    fontSize(self.fntSize)
    -- use longest sound name for size
    
    local w,h = textSize("longesttitleeverihaveseen") 
    w = w + 20
    h = h + self.height
    
    frmtextField.size = vec2(w-10,h)
    totextField.size = vec2(w-10,h)
    
    if string.len(self.title) == 1 then
        if self.title == "X" then
            w = textSize("BS") +20
        else 
            w = textSize("BS") + 40
        end
    end
    
    if self.title == "Back" or self.title == "Hide" then
        w = textSize("Hide") +20
    end
    
    
    roundRect(self.pos.x - w/2, self.pos.y - h/2, w,h,10) 
    if currentMenu == categoryMenu then
        fill(50, 63, 79, 255)
        roundRect(self.pos.x - w/2+5, self.pos.y - h/2+5, w-10,h-10,10)
    end
    self.size = vec2(w,h)        
    textMode(CENTER)
    fill(self.fr,self.fg,self.fb,self.fa)
    text(self.title,self.pos.x,self.pos.y)

    popStyle()
end

function Buttons:buttonAction(title)
    if (title == "Area") then
        currentMenu = areaMenu
        currentMenu:onEnter()
    elseif (title == "Length") then
        currentMenu = lengthMenu
        currentMenu:onEnter()
    elseif (title == "Temperature") then
        currentMenu = temperatureMenu
        currentMenu:onEnter()
    elseif (title == "Speed") then
        currentMenu = speedMenu
        currentMenu:onEnter()
    elseif (title == "Volume") then
        currentMenu = volumeMenu
        currentMenu:onEnter()
    elseif (title == "Mass") then
        currentMenu = massMenu
        currentMenu:onEnter()
    elseif (title == "Main Menu") then
        currentMenu = categoryMenu
        currentMenu:onEnter()
    elseif (title == "Convert") then
        -- Here it checks if only one unit is selected from each side of the button sets then 
        -- it will assign the selected unit name to a global variable that can be used for        
        -- triggering the corresponding conversion method
        if (frmUnitcounter==1 and toUnitcounter==1) then
            self:setUnit()
            toValue = Conversion(currentMenu.title)
        else
            screenMsg = "Select one unit from each side. If more than one unit is selected then tap to unselect"
        end
    elseif (title == "Hide") then
        keyFlag = false
        alp=0
    elseif (title == "Back") then
        if string.len(frmValue) == 1 then
            if frmValue == "." then
                dotCounter = dotCounter -1
            elseif frmValue=="-" then
                signCounter = signCounter - 1
            end
            frmValue = "0"
        elseif string.len(frmValue) > 1 then
            t = string.sub(frmValue,1,string.len(frmValue)-1)
            d = string.sub(frmValue,string.len(frmValue),string.len(frmValue))
            if d=="." then
                dotCounter = dotCounter-1
            end
            frmValue = t
        end
    elseif (title >="0" and title <="9") or title =="." or title =="-" then
        if title=="."then
            if dotCounter == 0 then
                dotCounter = dotCounter + 1
            else
                title = ""
            end
        end
        
        if title=="-"then
            if signCounter == 0 then
                signCounter = signCounter + 1
            else
                title = ""
            end
        end
            
        if frmValue == "0" then
            frmValue = ""
            frmValue = frmValue .. title
        else
            frmValue = frmValue .. title
        end
        
    elseif (title == "X") then
        close()
    else
        screenMsg = "Select one unit from each side for conversion"
       -- frmValue = "0"
       -- toValue = ""
        dotCounter = 0
        signCounter = 0
        
    end
    
    
end

-- This function increase 2 counters when any unit button is selected in submenu and decrease 
-- when it is unselected. these counters are used to confirm only one unit is selected from 
-- each side of the menu at the time of conversion
function Buttons:counter(side,m)
    if (m=="check") then
        if(side==1) then
            frmUnitcounter = frmUnitcounter + 1
        elseif(side==2) then
            toUnitcounter = toUnitcounter + 1
        end
    elseif(m=="uncheck") then
        if(side==1) then
            frmUnitcounter = frmUnitcounter - 1
        elseif(side==2) then
            toUnitcounter = toUnitcounter - 1
        end
    end
    
    if (frmUnitcounter>1 or toUnitcounter >1 or frmUnitcounter == 0 or toUnitcounter == 0) then
        frmUnit =""
        toUnit = ""
    end
end

function Buttons:setUnit()
    for _, v in pairs (currentMenu.fromButtons) do
        if (v.selectFlag) then
            frmUnit = v.title
        end
    end
    for _, v in pairs (currentMenu.toButtons) do
        if (v.selectFlag) then
            toUnit = v.title
        end
    end
end

function Buttons:touched(touch, sel)
    -- Codea does not automatically call this method
        if touch.state == ENDED and self:hit(vec2(touch.x,touch.y)) then
            -- It only allow to change the color of 2 sets of unit buttons and its font in submenu 
            -- when any these are selected and unselected
           if (sel==1 or sel == 2)then
                if (self.selectFlag == false) then
                    self.selectFlag =true
                    self.color=color(22, 188, 212, 255) -- Changes color on select
                    fill(0, 0, 0, 255)
                    self.fr, self.fg, self.fb, self.fa = fill() -- Changes font color on select
                    self:counter(sel,"check")
                else
                    self.selectFlag = false
                    --Changes color of button to its original color on unselect
                    self.color = color(self.r,self.g,self.b,self.a) 
                    --Changes font color of a selected button to its original color on unselect
                    fill(tr,tg,tb,ta)
                    self.fr, self.fg, self.fb, self.fa = fill()
                    self:counter(sel,"uncheck")
                end
           end
            if self.action then
                self.action()
            end
        end
   
end

TextField

TextField = class()

function TextField:init(title)
    -- you can accept and set parameters here
    self.title = title
    self.pos = vec2(0,0)
    self.size = vec2(0,0)
    self.color = color(123, 124, 129, 255)
    
end


function TextField:draw()
    -- Codea does not automatically call this method
    
    fill(self.color)
    strokeWidth(3)
    stroke(190, 190, 190, 255)
    rectMode(CENTER)
    rect(self.pos.x,self.pos.y,self.size.x,self.size.y)
    strokeWidth(2)
    stroke(161, 162, 163, 255)
    rect(self.pos.x,self.pos.y,self.size.x-4,self.size.y-4)
    if self.title == "From" then
        fill(255, 255, 255, 255)
        font("Futura-CondensedMedium")
        fontSize(30)
        textMode(CENTER)
        text(frmValue,self.pos.x,self.pos.y)
    elseif self.title == "To" then
        
        fill(255, 255, 255, 255)
        font("Futura-CondensedMedium")
        fontSize(30)
        textMode(CENTER)
        text(toValue,self.pos.x,self.pos.y)
    end
end

function TextField:hit(p)
    local l = self.pos.x - self.size.x/2
    local r = self.pos.x + self.size.x/2
    local t = self.pos.y + self.size.y/2
    local b = self.pos.y - self.size.y/2
    if p.x > l and p.x < r and
       p.y > b and p.y < t then
        return true
    end
    
    return false
end

function TextField:touched(touch)
    -- Codea does not automatically call this method
    if touch.state == ENDED and self:hit(vec2(touch.x,touch.y)) then
        self.color = color(68, 72, 72, 255)
        toValue=""
        keyFlag = true
    end
end

CategoryMenu


CategoryMenu = class()


function CategoryMenu:init(title,fnt,h)
    -- you can accept and set parameters here
    self.title = title
    self.height = h
    self.category = {"Area", "Length", "Temperature", "Mass", "Speed", "Volume"}
    self.buttonFontsize = fnt
    self.buttons = {}
    
    fill(150, 173, 179, 255)
    local r,g,b,a = fill()
    fill(255, 255, 255, 255)
    local fr,fg,fb,fa = fill()

    for _, v in pairs (self.category) do
        table.insert(self.buttons, Buttons(v,r,g,b,a,fr,fg,fb,fa,self.buttonFontsize,self.height))
    end
    
    catBut = self.buttons
end

function CategoryMenu:onEnter()
    clearParameters()
    clearOutput()
    xp = WIDTH
    --displayMode(FULLSCREEN)
end

function CategoryMenu:draw()
    -- Codea does not automatically call this method
    background(255, 255, 255, 255)
    spriteMode(CENTER)
    sprite("SpaceCute:Background",WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
    pushStyle()
    fontSize(50)
    font("HelveticaNeue-CondensedBlack")
    
    fill(216, 215, 217, 255)
    text("Unit Converter", WIDTH/2+2, HEIGHT-62) 
    fill(35, 83, 136, 255)  
    text("Unit Converter", WIDTH/2, HEIGHT-60) 

    buttonfntSize = 25
    self.drawButtons()

    popStyle()
    
end

function CategoryMenu:drawButtons()
    
    local top = HEIGHT - (HEIGHT*0.30)
    local left = WIDTH/2
    
    fontSize(25)
    fill(51, 57, 58, 255)
    text("Select a category for unit conversion", WIDTH/2+1, HEIGHT-101)
    fill(185, 195, 197, 255)
    text("Select a category for unit conversion", WIDTH/2, HEIGHT-100)
   
    for _,b in pairs(catBut) do
        b.pos = vec2(left,top)
        b:draw()
        top = top - b.size.y - 5
    end
    
end

function CategoryMenu:touched(touch)
    -- Codea does not automatically call this method
        for _,b in pairs(catBut) do
            b:touched(touch,0)
        end
end

Function - roundRect

function roundRect(x,y,w,h,r)
    pushStyle()
    
    insetPos = vec2(x+r,y+r)
    insetSize = vec2(w-2*r,h-2*r)
    
    --Copy fill into stroke
    local red,green,blue,a = fill()
    stroke(red,green,blue,a)
     
    noSmooth()
    rectMode(CORNER)
    rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
    if r > 0 then
        smooth()
        lineCapMode(ROUND)
        strokeWidth(r*2)

        line(insetPos.x, insetPos.y, 
             insetPos.x + insetSize.x, insetPos.y)
        line(insetPos.x, insetPos.y,
             insetPos.x, insetPos.y + insetSize.y)
        line(insetPos.x, insetPos.y + insetSize.y,
             insetPos.x + insetSize.x, insetPos.y + insetSize.y)
        line(insetPos.x + insetSize.x, insetPos.y,
             insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
    end
    popStyle()
end

I deleted all the blank posts - please check that I didn’t inadvertently delete something important.

Great work, thanks for sharing.

Thanks the Textfield class save hours of coding!

You are welcome edwin809. I tried to implement cursor effect in it but didnt work well though. May be u can try.