Soda v0.7: gorgeous and powerful GUI/ windowing/ button library. Now with fully selectable text.



  • i had same bad experience as hpsoft (ipad air). jvm38 correction fixes it for me.

  • RcvRcv
    Posts: 5

    @yojimbo2000. Thanks for the correction in the scrool tab you are posted.
    This correction fixed the problem.

    This was the problem i have reported. Sorry for my bad english.

    Great job.

    Thank you again.

  • Posts: 257

    with the @Jmv38 correction soda 0.3 is perfect thanks :)>-

  • edited September 2015 Posts: 2,020

    Oh I didn't see @Jmv38 's post. I wrote mine at the same time, and then the page flicked over.

    Yes, that's how to make the scroll gesture rejection less aggressive.

    In the previous versions of Soda I experimented with different values there. It used to be if self.touchMove<0.1 (touchMove always has maths.abs values added, so you don't need another maths.abs when you query it)

    @hpsoft could I ask you to try putting 1 and 0.1 (as well as 10 suggested by @Jmv38), and let me know which "feels" best?

    For now, I'll update the code with if self.touchMove < 1.

    Thanks to @Jmv38 @hpsoft @Rcv for testing and suggestions

  • Posts: 2,020

    I guess the lesson here is that different iPads (and different fingers!) return different parameters. I guess we discovered this back when we had the "fat finger contest".

  • Posts: 257

    For me with ipad air 128mb and ios 8.3 from 5 it works. but i prefer 10.
    it 's catastrophic between 0.1 and 3.
    with value = 4 it works one in two.

  • Posts: 2,020

    @hpsoft that's really interesting. Thanks for the feedback. Ok, I'll change it to 10.

  • Posts: 2,020

    V0.4 is out, with quite a few fixes and additions. The tutorial is now complete, it covers most of the element types.

    Release notes:


    • Vertical list selectors now less aggressive at rejecting scroll gestures
    • Lists now have :clearSelection() method
    • Lists now have an enumerate flag. Set this to true to automatically number the items in the list
    • To populate a TextScroll or TextWindow with text, constructor argument is now textBody, instead of text
    • Soda.Alert2 - 2-Button proceed/ cancel alerts have been fixed
    • Fixed a bug in the appearance of buttons in alerts
    • Close button is now a proper X
    • Selector touch logic now greatly simplified
    • Tutorial now updated with all major element types

    The GitHub link again:

  • Posts: 2,020

    V0.5 is out at the same link, it now has sliders!


    • NEW Soda.Sliders - Sliders can be any length (default to 300 pixels), can be integer or floating point, can have an optional set of "snap points" that the value will snap to, support tapping either side of the handle for fine +/- adjustments, and at slow drag speeds have a cubic relation to touch allowing for up to 1/20,000 accuracy.

    • New, more comprehensive overview of all the elements supported by Soda when you run the program.

    • In TextScroll and TextWindow, method inputString(text) now appends text to whatever is already in the window, instead of clearing the window. New method clearString() clears the window.

    • Panel switching functionality of Soda.Segment now also available in Soda.List via panels parameter

    • Soda.Window can now have optional automatic ok and cancel buttons by setting ok and cancel attributes.

    Sliders Sliders

  • sliders, wonderful! Now i can upgrade my soft to use Soda. Many thanks for sharing.

  • edited September 2015 Posts: 2,020

    Next up, I'd like to try to sort out styles, which are a bit messy at the moment. I'm thinking it would be great if styles could "cascade", so that children inherit the styles of parents. So you set a parent window to a "dark" or "light" preset, and that automatically changes the text colour of the children so that it remains legible. You can't just use the pushStyle() popStyle() stack, because of course you need the background shape and the text to have a different fill. I'm thinking then of substituting the stack for a Soda style stack that has more options, eg fontFill (for text fill).

    Suggestions welcome.

  • Jmv38Jmv38 Mod
    edited September 2015 Posts: 3,297

    the problem with style cascade is:
    - easy to use parent style as child creation or linking.
    - difficult if you want that when you change the parent, the child follows. Passing everything through may be too cumbersome (too many parameters). Or you must trigger a cascade of updates, but how do you managed children specifics then?

    great work!

  • Sorry, but i don't get it, how this sort of construction works?

    local a = b.c{...}  

    Someone please give me link to some matireals or tutorial for this structure.

  • edited September 2015 Posts: 2,020

    @Mikewin In Lua, if a function takes a single table or a single string as its argument, then the () brackets that usually enclose the arguments can be omitted. So, Soda.Button{title = "Press Me"} is the same as Soda.Button({title = "Press Me"}), but with less typing

    Soda.Button in this case is just the name of the function or class. I could have just called the classes Button TextEntry etc, but I decided to put the entire library inside the table Soda to minimise the risk of the user accidentally overwriting some of the classes. As long as you don't have a variable called Soda, you don't need to worry about overwriting any of it.

  • Posts: 2,020

    @Mikewin And here's a nice tutorial explaining why you might want to pass a function a table of named arguments myFunction{x = 5, y = 10} rather than the standard Lua positional arguments myFunction(5, 10):

  • @yojimbo2000 thanks for explanation

  • Posts: 2,020

    @Mikewin No problem.

    Does anyone here use Pythonista, particularly the ui module, and the interface designer? I was playing around with it and it seems pretty cool. I was wondering if anyone would be interested in a similar designer and/or a system to keep most of the UI code out of the way?

  • @yojimbo2000
    Here in Soda.lua

    function Soda.touched(t)
        local tpos = vec2(t.x, t.y-Soda.UIoffset)
        for i = #Soda.items, 1, -1 do --test most recent item first
            local v = Soda.items[i] 
            if v:touched(t, tpos) then return end

    You will have bug that if in some v:touched call some member of Soda.items will be deleted than scrolls will crush. Or may not, that depend on your luck and position of deleted member.

  • Posts: 2,020

    @Mikewin no I don't think so. Iterating backwards through an array is usually regarded as safe if an item gets removed (table length will shorten, but that doesn't matter because you're heading for the start of the table). In any case, deletion of items never happens in the touched function, it is always deferred to the draw loop (you set the kill flag to true). table.remove during ipairs is also safe, because ipairs is an iterator that checks the length of the table each cycle.

  • Posts: 2,020

    v0.6 is out. The main change is a new style engine that makes it easier to create consistent and legible interfaces. I used Adobe kula to copy the iOS 9 colours. Tutorial has been rejigged. I also added a cool calculator demo inspired (cough) by the one that ships with Pythonista:


  • Posts: 257

    I like slider with snap points and adjustments ( very usefull )

    I've tested the fast calculator ( in Soda 0.6 ) :)>-

    When i try the calculator in SodaTuto 0.6

    I've an error :

    string "function overview(t) " ]:62: attempt to index a nil value ( field 'window' )

    I was wondering if i could indicate a specific color with Substyle

    Thanks for this very good update

  • RcvRcv
    Posts: 5

    Sorry, how to correct the resolución in iPhone 6

    Thank for your great Job

  • edited October 2015 Posts: 2,020

    @hpsoft thanks for the bug report, I'll look into that

    @Rcv I haven't implemented universalisation yet, though I am experimenting with it, it's on the roadmap. One thing v0.6 does do that I forgot to mention, is that all font sizes are now scalar. So you can scale all the font sizes by setting the variable Soda.baseFontSize. The default is 20. Some of the classes (eg switch) have default sizes that are hard-coded in pixels. I need to make those scalar too.

    The plan would be to have an optional interface testing function built right in, so you can select iPhone 6 or whatever and see whether/ how the interface scales.

    In my initial tests though, simply shrinking everything doesn't look good or make for a useable interface, which is why Apple requires that apps have a different layout for iPhone and iPad.

    I could add a device testing function, using deviceMetrics to work out which device it is. But then it would be up to you to implement different interfaces depending on device.

    Universal app designers: do these proposed features sound helpful? Are special steps needed on the XCode side of things for creating universal apps in Codea?

  • Posts: 2,020

    @hpsoft to fix that bug you need to cut the line calculator.init() from the setup function of the main Soda program, in the Main tab, and paste it as the last line of the overview function, at the very bottom of the overview function.

    Regarding custom styles, I'll update the documentation on this shortly.

  • RcvRcv
    Posts: 5

    @yojimbo2000 Thank you for your fast answer.

  • Posts: 257

    @yojimbo2000 thanks for bugs and custom styles

  • Posts: 257
    NNvar=Soda.TextEntry { parent=tvmpanel, x=10, y=-30, w=0.5, h=40, 
          title = "N =  ", default = ""..NN,
          callback = function(self, inkey) NN=inkey NN=tonumber(NN) end
        IYRvar=Soda.TextEntry { parent=tvmpanel, x=10, y=-80, w=0.5, h=40,
          title = "I/YR = ", default = ""..IYR,
          callback = function(self, inkey) IYR=inkey IYR=tonumber(IYR) end
        PVvar=Soda.TextEntry { parent=tvmpanel, x=10, y=-130, w=0.5, h=40,
          title = "PV = ", default = ""..PV,
          callback = function(self, inkey) PV=inkey PV=tonumber(PV) end
        PMTvar=Soda.TextEntry { parent=tvmpanel, x=10, y=-180, w=0.5, h=40,
          title = "PMT = ", default = ""..PMT,
          callback = function(self, inkey) PMT=inkey PMT=tonumber(PMT) end
        FVvar=Soda.TextEntry { parent=tvmpanel, x=10, y=-230, w=0.5, h=40,
          title = "FV = ", default = ""..FV,
          callback = function(self, inkey) FV=inkey FV=tonumber(FV) end
        PYRvar=Soda.TextEntry { parent=tvmpanel, x=10, y=-280, w=0.5, h=40,
          title = "P/YR = ", default = ""..PYR,
          callback = function(self, inkey) PYR=inkey PYR=tonumber(PYR) end
        Soda.Switch{ parent=tvmpanel, x = 10, y = -330, title = "Début / Fin" }
            parent = tvmpanel, x = 430, y = -30, w = -10, h = 40,
            title = "Compute",
            text = {"FV","PV","PMT","IYR","N","BUG"},
            defaultNo = 1,
            callback = function(self, selected,text)
                computenum= selected  --  le numéro de l'item selectionné
                computetext = text  -- Le titre selectionné
                if computetext=="FV" then
                   FV=PV*(1+IYR/100)^NN ; FV=math.floor(FV*100)/100 ; FVvar:inputString(FV.."")
                if computetext=="PV" then
                   PV=FV/(1+IYR/100)^NN ; PV=math.floor(PV*100)/100 ; PVvar:inputString(PV.."")
                if computetext=="PMT" then
                   PMT=PV*(IYR/100*(1+IYR/100)^NN)/((1+IYR/100)^NN-1) ; PMT=math.floor(PMT*100)/100
                if computetext=="IYR" then
                   IYR=((FV/PV)^(1/NN)-1)*100 ; IYR=math.floor(IYR*100)/100 ; IYRvar:inputString(IYR.."")
                if computetext=="N" then
                    NN=math.log(FV/PV)/math.log(1+IYR/100) ; NN=math.floor(NN*100)/100
        Soda.TextEntry { parent=cfpanel, x=10, y=-30, w=0.5, h=40, title = "CF0 = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-80, w=0.5, h=40, title = "I = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-130, w=0.5, h=40, title = "NPV = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-180, w=0.5, h=40, title = "NFV = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-230, w=0.5, h=40, title = "PB = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-280, w=0.5, h=40, title = "DPB = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-330, w=0.5, h=40, title = "IRR = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=10, y=-380, w=0.5, h=40, title = "RI = ", default = "0"}
        Soda.TextEntry { parent=cfpanel, x=430, y=-30, w=380, h=40, title = "MOD = ", default = "0"}
            parent = cfpanel, x = 430, y = -80, w = -10, h = 40,
            title = "Compute",
            text = {"CF0","I","NPV","NFV","PB","DPB","IRR","RI","MOD"},
            defaultNo = 1,
  • edited October 2015 Posts: 257

    Here are some of the code of my project.

    But i've 2 problems :

    1 ) When I change a value this value is changed if I type enter or if I type on the keyboard
    but not if I point with my finger on another field

    2 ) My second problem :
    I had to add "bug" item in Soda.DropdownList
    because ( it's difficult for me to explain in english )
    here for example : defaultNo = 1 correspond to the first item "FV"
    if i want to execute FV i must choise BUG then i can execut FV

       if i want test with n=5 then n=6 then n=8 ...
       and compute everytime fv result, I have to go through BUG item (wich execute nothing)
         To sum up,  i can't excut again fv ( and it's the same problem for other item )
    Thanks for your attention
  • Posts: 2,020
    1. Yes, this is intentional. It's a kind of cancel text input behaviour. But maybe that's confusing, as it's not standard. I'll change it so that selecting a different element is the same as hitting return or hide keyboard

    2. In 0.6 defaultNo was changed to default as part of an effort to make the attributes consistent (and hopefully easier to remember) across different classes. Sorry, I'll add a note about this to the version notes, and I'll try not to break the interface too much in future.

    Thanks for the reports, and let me know whether using default fixes your problem.

  • edited October 2015 Posts: 257

    thanks for 1)

    for 2) it does not change with default. I already had the problem with the version 0.5

  • Posts: 2,020

    @hpsoft I pasted the code snippet above into your earlier equations program, but I'm not seeing the issue you described. I took out the "BUG" option, and I was able to select any of the calculations, and it seemed the correct calculation is performed. Is it to do with having or not having a default? If you omit the default attribute, then the list starts without any selection, and the callback is not called (this is the behaviour you want, I expect). If default is set, that item will be selected, and the callback will be called, when the list is created.

  • edited October 2015 Posts: 257

    my ios version is 8.3 ( not yet 9 )
    yes i don't use callback here
    the result is returned directly in textentry
    i've removed "bug" item and replaced defaultNo by default

    by default, FV is selected in Dropdownlist

    i tape 5 enter in N
    i tape 6 enter in IYR
    i tape 100000 enter in PV

    but when i want select FV
    i can't validate FV

    i can validate the other PV N IYR...

  • Posts: 2,020

    Try removing the default = 1 attribute altogether.

    I think the issue could be that if you select an option that is already selected, it does nothing, the callback does not fire again (this is by design). ie the list is for selecting one option from many. It might be more appropriate in this case to have a set of buttons for each calculation, like a calculator.

  • edited November 2015 Posts: 2,020

    Just over a month ago, @Jmv38 messaged me with an idea to merge his Sensor class for differentiating between various touch gestures into Soda. I was impressed with the effect this had in terms of making the code more modular by putting all of the touch logic in one class. So, 5 or so weeks later, and a lot of hard work (mainly on @Jmv38 's part) here's v0.7 of Soda. @Jmv38 rewrote the text entry class, so that it now has fully selectable text with draggable in and out points and an iOS style popover menu for cut paste, undo etc.

    See below for version notes and screen shots.

    It was a fascinating experience for me, going from being the "developer" to more of a "project manager" role while @Jmv38 did all the heavy lifting. All of the source control was done with Working Copy (and the Working Copy Codea Client) which was very useful, particularly in the early stages. @Jmv38 initially forked v0.4 of Soda, so I was able to continue working on the next 2 versions at the asme time as @Jmv38 was working on the Sensor branch, and then merge in the differences. Thanks @Jmv38 for all your work.



    • Big changes underneath the hood: all touch logic is now handled by Jmv38's Sensor engine (renamed Gesture in Soda), making for a more modular architecture that will be easier to maintain and expand in future.

    • Jmv38 has rewritten the text entry boxes. Now features fully selectable text with draggable selection start and end points, the ability to scroll the text by dragging and holding the cursor to either end of the entry box, and a pop-up menu for cut, copy, paste, and 5-level undo.

    • Windows can now be draggable: add draggable = true to the window's constructor to add this. The window can be moved by dragging any part of the window that does not contain interface elements. See the calculator demo for an example of this.

    • Various minor fixes for iOS 9 and Codea 2.3.2 affecting sliders, orientationChanged, and forcing the plain, non-emoji form of various symbols to display.

    Selectable text

    Calculator in a draggable window

  • IgnatzIgnatz Mod
    Posts: 5,396

    @yojimbo2000, @Jmv38 - great work, guys! =D>

  • Posts: 154

    Very impressive, guys!
    Question: non-english keyboards don't seem to work with text entry boxes... Is that a bug or just not implemenfed yet (i tried with Russian keyboard)

  • Jmv38Jmv38 Mod
    Posts: 3,297

    @Juce did it work with previous version of Soda?

  • Posts: 154

    @Jmv38, it works in v0.5, although it has some issues there: the font size appears to be different (larger) compared to latin chars, so as you type more text cursor gradually gets out of position, and eventually that appears to cause runtime errors. With v0.7 (head of master), cyrillic symbols don't render at all, and cursor does not move.

  • even more gorgeousness! great job

  • Posts: 2,020

    @piinthesky thanks!

    @juce thanks for reporting this. Can I ask, in utf-8 is Russian double/ multi-byte? We're wondering if this issue is to do with the Lua string commands being set up for single-byte characters

  • Posts: 154

    @yojimbo2000, in utf-8, it is multi-byte, yes... You are right: that appears to be the root of the problem. For example, this prints out "12", even though, the word has only 6 characters.

    -- Russian
    function setup()
  • Posts: 2,020

    @juce We'll get a fix out for this, probably using the Lua 5.3 UTF8 library. Thanks again.

  • edited November 2015 Posts: 257

    @jmv38 and @yojimbo2000 thank you for soda and excellent copy/paste wich works fine with ios 8. It's a dream team.

    but i have 2 problem :

    • the first , the cursor is misplaced if i use TextMode(), the solution i use pushstyle and popstyle ( not the case in previous version )

    • the 2nd problem when i use Soda.TextEntry, i have the error " attempt to concatenate a nil value " in the last version ( it's works fine with the previous version ).

    an extract :

    variable=Soda.TextEntry { parent=panelnew, x=20, y=-20, w=600, h=40, 
          title = "Variable =", default = "", callback = function(self, inkey) v1=inkey end
        valeur=Soda.TextEntry { parent=panelnew, x=20, y=-80, w=600, h=40, 
          title = "Valeur =", default = "", callback = function(self, inkey) v2=inkey end
    function draw()
        pushMatrix() ; ; Soda.drawing() ; popMatrix()
            textMode(CORNER) ; font("Futura-CondensedMedium") ; fontSize(30)
            varlist=listLocalData() ; nbvar=#varlist ; text("Nb de variables : "..nbvar,50,700)
            if ecran=="Info" then end


  • Jmv38Jmv38 Mod
    Posts: 3,297

    @hpsoft thanks for your input. I'll check for this and correct it.

  • Posts: 257

    thank you it will be very usefull for my database project including photo links :)

  • @juce привет, как дела? Я тоже русский.
    -- @juce hello, how are you, I am Russian, too

  • Can you do it in one class? I am too lazy to do this

  • edited November 2015 Posts: 2,020


    According to your various other posts on different threads, you want text input, switches, and various other elements, so no, it cannot be all in one class.

    Try to keep your comments constructive or you will be blocked.

  • Posts: 154

    @TokOut, привет. Давай уж как-нибудь яснее выражайся - пиши вопросы по-русски. (Translation: @TokOut, you can write the questions in Russian)

  • IgnatzIgnatz Mod
    edited November 2015 Posts: 5,396

    Thanks, @juce!!

Sign In or Register to comment.