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

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

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.

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.

@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.

@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):

http://www.lua.org/pil/5.3.html

@yojimbo2000 thanks for explanation

@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
    end
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.

@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.

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:

calculator

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

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

Thank for your great Job

@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?

@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.

@yojimbo2000 Thank you for your fast answer.

@yojimbo2000 thanks for bugs and custom styles

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" }
    
    Soda.DropdownList{
        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.."")
            end
            if computetext=="PV" then
               PV=FV/(1+IYR/100)^NN ; PV=math.floor(PV*100)/100 ; PVvar:inputString(PV.."")
            end
            if computetext=="PMT" then
               PMT=PV*(IYR/100*(1+IYR/100)^NN)/((1+IYR/100)^NN-1) ; PMT=math.floor(PMT*100)/100
               PMTvar:inputString(PMT.."")
            end
            if computetext=="IYR" then
               IYR=((FV/PV)^(1/NN)-1)*100 ; IYR=math.floor(IYR*100)/100 ; IYRvar:inputString(IYR.."")
            end
            if computetext=="N" then
                NN=math.log(FV/PV)/math.log(1+IYR/100) ; NN=math.floor(NN*100)/100
                NNvar:inputString(NN.."")
            end
        
        end
    }

    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"}
    
    Soda.DropdownList{
        parent = cfpanel, x = 430, y = -80, w = -10, h = 40,
        title = "Compute",
        text = {"CF0","I","NPV","NFV","PB","DPB","IRR","RI","MOD"},
        defaultNo = 1,
    }

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
  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.