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

edited November 2015 in Code Sharing Posts: 1,972

We seem to have a number of button/window GUI libraries popping up at the moment, spurred on by the discussions started (or restarted) by @Jmv38

Here's my effort. Suggestions, contributions, pull requests etc welcome.

https://github.com/Utsira/Soda

Soda

Gorgeous and powerful GUI/ button library for Codea projects.

I'm calling it Soda as it is inspired by Cider (but is not a fork).

Features

Simple but powerful parent-child relationship between interface elements

  • Positions and dimensions of elements are defined relative to the parent of the element (ie the frame or window that they are in). Positions can be defined relative to any edge of the enclosing frame, or as a proportion of the parent frame. Decide a window is too cluttered? No need to move all of the elements around, just resize the parent window.

  • Elements automatically and intelligently resize when device orientation changes

  • A collection of elements can be moved around the screen, hidden or made inactive, just by addressing the parent of the collection

  • Drawing and touching are automatically handled in order to get the correct draw and touch order so that, for instance, it is not possible to touch an element through an overlying window.

Gorgeous and fast-performing graphics

  • 196-sample Gaussian blur shader used for proper drop-shadows and blurred panel effects

  • True rounded-rectangle mesh with an anti-aliased stroke, allows for translucent rounded-rectangles

  • Graphics kept largely separate from button logic (in the Style tab), making Soda very easily customisable and skinnable

Interface elements currently supported:

  • Frame. A container for other UI elements (a window).

  • Button. One press to activate a callback. Has a variety of built-in defaults for frequently-used interface elements such as the settings gear, the hamburger menu button etc.

  • Switch. Toggles on and off.

  • Segment. Horizontally segmented buttons that activate different frames/ panels.

  • List. A vertically scrolling list of elements that the user can select from. Has elastic snap-back when user scrolls past edge of list. Can easily be set up as a drop down list for auto-populating a text field.

  • TextEntry. A text entry field with a touchable cursor, and scrolling if the input is too long for the field.

  • TextWindow. A window for handling scrolling through large bodies of text.

  • Various alerts and dialogs.

Roadmap

  • Sliders.

  • Improvements to TextEntry:

    • Be able to select a word with a double-tap, or the entire field with a triple-tap.

    • Be able to scroll the field leftwards by moving the cursor (currently you can only scroll leftward by deleting)

  • Add a factory for easier creation of the drop-down list seen in the demo

Screenshots

various buttons Various buttons in a segmented panel

blurred alert An alert dialog with a blurred effect

DropDownList A drop down list for autopopulating a text field

TextWindow A window for scrolling through large bodies of text

Installation

Copy the contents of /SodaInstaller.lua. It is easiest to do this from the RAW page:
https://raw.githubusercontent.com/Utsira/Soda/master/SodaInstaller.lua
In Codea, long-press "+ Add New Project", and select "Paste into project".

«134

Comments

  • IgnatzIgnatz Mod
    edited September 2015 Posts: 5,396

    It's not often I see something and think "I have to have that". B-) ^:)^

  • Posts: 1,972

    @Ignatz thanks! Suggestions welcome. It still has a few quirks.

  • edited September 2015 Posts: 1,972

    Speaking of quirks, I just pushed an update that (hopefully?) squashed a bug where sometimes the vertical lists would return the wrong selection.

  • This puts my code to shame

  • Posts: 1,972

    @FLCode nonsense! We all borrow from and are inspired by each other. I started writing this after using Cider, and also being inspired by the discussion of specifications on @Jmv38 's thread. And it adapts @LoopSpace 's rounded rect mesh, etc etc.

  • Jmv38Jmv38 Mod
    edited September 2015 Posts: 3,265

    your screenshots are awsome! =D>
    i've downloaded the code (thanks to Juice Gister) and it works perectly!
    Just what we needed, thanks.

  • yippie, gorgeous indeed!

  • Posts: 257

    very beautifull

  • IgnatzIgnatz Mod
    Posts: 5,396

    Wrt documentation, it's much easier to learn something if it slopes upward gradually and is not just a cliff!

    I suggest a layered approach, starting with the simplest possible examples that make it easy to use Soda in seconds, then adding features layer by layer, with your current documentation as the ultimate reference.

    A nice way to do it may be a set of progressively more complex examples in tabs, with a selector parameter to move between them. There are some examples of these "step by step" projects in the wiki, and I can point you to good code for managing it, which was developed some time back by various people.

  • gorgeous and powerful

  • And the winner is... :)

    Well done - looks a LOT nicer than my UI library

  • Once this is fully documented, I'd want to see it as a built-in example, it's so useful. I'll definitely be using it in some of my personal projects, since it's so beautifully designed.

  • Posts: 1,972

    @Ignatz

    That's a good idea. Here's a screenshot of what I have in mind:

    tutorial

    You select stages of the tutorial from the menu on the left. These gradually build up the demo step-by-step, and correspond to the tabs in the source code. The (extensively documented) source code is shown in the console at the bottom, and the results of that code appear in the top right.

    I might add Codea syntax highlighting to the TextWindow element to make it clearer...

    The tutorial is a separate project that uses Soda as a dependency.

    Would this be a useful way to document it do you think? Comments and suggestions welcome.

    I also added a "known issues" section to the GitHub readme. There's 2 bugs that I'll try to track down.

  • IgnatzIgnatz Mod
    Posts: 5,396

    Yes, I think that's the best way.

    Right now, the instructions are rather overwhelming. It would be nice to be able to just pick up a template off one of your tabs, so I would create some common use cases. Ideally the code in each tab should be standalone. (The code I'm talking about sharing with you allows you to have a setup and draw in each tab, and has just one extra line of code at the top, plus some special code in the main tab).

  • Posts: 1,972

    @Ignatz cool, point me to that code. Although with Soda, the idea is that draw, touched etc stay the same. The only thing the user needs to write is the button constructors. In the tutorial, the only thing that will be different between the different tabs is the button setup function. The Main tab will act as a template, ie with the bare minimum needed to use Soda in setup, draw, touched, keyboard, and orientationChanged.

  • Posts: 680

    @yojimbo2000 - this is superb! One minor thing - on my iPad the ? button character is not centred. Don't know why as it was a straight copy and paste of the code - no modification. I'm on an iPad 3 so may be a legacy issue, or maybe you're on a prerelease version of IOS.

  • edited September 2015 Posts: 1,972

    @West that's weird, I'll look into that. It's the full-width question mark (makes it slightly wider than a regular one) encoded with Lua unicode. Perhaps not all iPads support the same sets of uni code symbols. The other buttons all look ok in your video though. In the Button tab, in Soda.Querybutton, try changing "t.title" from the unicode to just a regular "?" t.title = "?", and see if that displays centred?

  • Posts: 680

    Yes that fixed it. The greater than/less than symbols are ever-so-slightly lower than centre, but when I replace by a standard "<" it's even lower. This is because < is a lowercase symbol in this context so even though the character is correctly centred there is a wee bit of white space above it. Is there an uppercase < symbol I wonder?

  • Hi @yojimbo2000,

    This is fantastic, I just finished rewriting my UIManager, but this is very slick indeed...

    I went down the route of having panels with layoutmanagers, e.g. flow and grid to auto-layout components.... I used a table definition as a DSL (domain specific language) to express the UI in a sort of 'config' block to create the hierarchical UI layout...

    One thing I found I needed was to express callbacks as the following structure:

    callback = {
        self = <an object, optional>,
        function = <a class function or anonymous function>,
        data = <some client data>
    }
    

    I then implemented this as:

    if(callback) then
        if(callback.self) then
            callback.function(callback.self, callback.data)
        else
            callback.function(callback.data)
        end
    end
    

    This then allows you to call back directly into another object function if desired, e.g. a game or view class that owns the UI...

    Great work,

    Brookesi

  • Posts: 1,972

    @brookesi that's an interesting idea with the callbacks, but you can do that anyway with closures, no?

    eg method of a class instance:

    callback = function(passSomeArgs) anInstance:aMethod(passSomeArgs, moreArgs) end

    or, if no arguments and no class instance need to be passed it can just be

    callback = table.withAFunction

  • Hi @yojimbo2000,

    Ah, ok, thought there may be upvalue issues or something, but that makes sense, nice!

    Brookesi

  • Posts: 1,972

    Yeah I didn't realise just how powerful closures were until I was researching coroutines. Closures remember the upvalues, in this case the class instance.

  • I think I've been using them without realising it ;)

    In my UIManager I have a rectangle class that uses global offsets as static members of the rectangle class,this is so I can animate flying the UI in or out from different directions, but then I realized that two UIs would share this offset and interfere with each other so I declared my class and all its functions as a local in a closure and returned that, so I can get a different 'version' of the class specific to each UI instance...

    All good fun...

    Brookesi

  • Nice! Really, very nice! But at the third screenshot Ice Man seems not to be blurred? Is this a bug, or a feature? And i think my implementation of text scrolling would be better here. An example here: https://gist.github.com/anonymous/d280e47c1a8346600b7e

  • Posts: 1,972

    @TimurEke the list panel in that screenshot is not set to be blurry, just translucent. Maybe it should just be solid colour. I'm not sure that the translucency looks that great on its own (without the blurriness).

    What's different with your text scrolling? It seems the same as mine, in terms of the algorithm for the inertia and the method of actually moving the text.

    Mine adds support for bodies of text that are taller than the screen (text doesn't display anything if what you're texting exceeds either the width or the height of the screen), and elastic snap-back (which is currently buggy if the text body is shorter than than the text box)

  • @yojimbo2000, Yeah, you are right I thought mine would be different ;)

  • SimeonSimeon Admin Mod
    Posts: 4,334

    @yojimbo2000 that's a stunning UI library you've made. So pretty, great work.

  • edited September 2015 Posts: 131

    @yojimbo2000, very well done! Looks beautiful and performs a lot better than i expected it would on my old iPad 2. And the demo is cool too: i wonder how people started by pressing the red button...

  • Posts: 1,197

    Beautiful work. Time to retire Cider... I'm going to use this myself!

  • Posts: 257
    function equations()
    
        var="FV"
    
        local panel = Soda.Control { 
            title = "Résolutions d'équations", hidden = true, x=50, y=160, w=900, h=500, 
            blurred = true, style = Soda.style.darkBlurred
        }
    
        local menu = Soda.MenuButton {x = -20, y = -20}
        menu.callback = function() panel:show(RIGHT) menu:hide(RIGHT) end 
    
        Soda.BackButton {
            parent = panel, direction = RIGHT, x = -20, y = -20,
            callback = function() panel:hide(RIGHT) menu:show(RIGHT) end
        }
    
        local panel1 = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local panel2 = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local panel3 = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local panel4 = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local panel5 = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local panel6 = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        Soda.Segment{
            parent = panel, x = 20, y = -80, w = -20, h = 40,
            text = {"TVM","Cash Flow","Bond","Dépréciation","Statistique","Eq2nd"},
            panels = { panel1, panel2, panel3, panel4, panel5, panel6
            }
        }
    
        Soda.TextEntry { parent=panel1, x=10, y=-30, w=400, h=40, title = "N = Nombre de paiements =  ", default = "0"}
        Soda.TextEntry { parent=panel1, x=10, y=-80, w=400, h=40, title = "I/YR = Intérêt / an = ", default = "0"}
        Soda.TextEntry { parent=panel1, x=10, y=-130, w=400, h=40, title = "PV = Present Value = ", default = "0"}
        Soda.TextEntry { parent=panel1, x=10, y=-180, w=400, h=40, title = "PMT = Paiement = ", default = "0"}
        Soda.TextEntry { parent=panel1, x=10, y=-230, w=400, h=40, title = "FV = Future Value = ", default = "0"}
        Soda.TextEntry { parent=panel1, x=10, y=-280, w=400, h=40, title = "P/YR = Nb Paiements / an = ", default = "12"}
        Soda.Switch{ parent=panel1, x = 430, y = -280, title = "Début / Fin" }
    
        Soda.TextEntry { parent=panel2, x=10, y=-50, w=280, h=40, title = "CF0 = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=10, y=-100, w=280, h=40, title = "I = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=10, y=-150, w=280, h=40, title = "NPV = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=10, y=-200, w=280, h=40, title = "NFV = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=10, y=-250, w=280, h=40, title = "PB = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=410, y=-50, w=280, h=40, title = "DPB = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=410, y=-100, w=280, h=40, title = "IRR = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=410, y=-150, w=280, h=40, title = "RI = ", default = "0"}
        Soda.TextEntry { parent=panel2, x=410, y=-200, w=280, h=40, title = "MOD = ", default = "0"}
    
        Soda.TextEntry { parent=panel3, x=10, y=-50, w=280, h=40, title = "SDT = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=10, y=-100, w=280, h=40, title = "CPN = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=10, y=-150, w=280, h=40, title = "RDT = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=10, y=-200, w=280, h=40, title = "RV = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=10, y=-250, w=280, h=40, title = "ACT = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=410, y=-50, w=280, h=40, title = "YLD = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=410, y=-100, w=280, h=40, title = "PRI = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=410, y=-150, w=280, h=40, title = "AI = ", default = "0"}
        Soda.TextEntry { parent=panel3, x=410, y=-200, w=280, h=40, title = "DUR = ", default = "0"}
    
        Soda.TextEntry { parent=panel4, x=10, y=-50, w=260, h=40, title = "SL = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=10, y=-100, w=260, h=40, title = "SYD = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=10, y=-150, w=260, h=40, title = "DB = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=10, y=-200, w=260, h=40, title = "DBX = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=10, y=-250, w=260, h=40, title = "SLF = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=300, y=-50, w=260, h=40, title = "DBF = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=300, y=-100, w=260, h=40, title = "LIF = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=300, y=-150, w=260, h=40, title = "MO1 = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=300, y=-200, w=260, h=40, title = "DT1 = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=300, y=-250, w=260, h=40, title = "CST = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=590, y=-50, w=260, h=40, title = "SAL = ", default = "0"}
       Soda.TextEntry { parent=panel4, x=590, y=-100, w=260, h=40, title = "YR = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=590, y=-150, w=260, h=40, title = "DEP = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=590, y=-200, w=260, h=40, title = "RBV = ", default = "0"}
        Soda.TextEntry { parent=panel4, x=590, y=-250, w=260, h=40, title = "RDV = ", default = "0"}
    
        Soda.TextEntry { parent=panel5, x=10, y=-50, w=280, h=40, title = "X = ", default = "{  }"}
        Soda.TextEntry { parent=panel5, x=10, y=-100, w=280, h=40, title = "Y = ", default = "{  }"}
    
        Soda.TextEntry { parent=panel6, x=10, y=-50, w=280, h=40, title = "a = ", default = "0"}
        Soda.TextEntry { parent=panel6, x=10, y=-100, w=280, h=40, title = "b = ", default = "0"}
        Soda.TextEntry { parent=panel6, x=10, y=-150, w=280, h=40, title = "c = ", default = "0"}
    
        Soda.Button{
        parent = panel1, title = "Calcul", style = Soda.style.warning, 
        x = -20, y = 20, w = 200, h = 40, 
        callback = 
            function()
                if var=="PMT" then end
                Soda.Alert1{ 
                    title = var.." = ", y=0.6, 
                    style = Soda.style.darkBlurred, blurred = true, 
                    alert = true
                }
            end
        }   
    
        local countyName = Soda.TextEntry{
            parent = panel1, x = 430, y = -30, w = 380, h = 40,
            shapeArgs = {corners = 1 | 2},
            title = "Calculer :", default = "FV", inactive = true
        }
    
        local counties = Soda.List{
            parent = panel1, hidden = true, x = 430, y = -70, w = 420, h = 200,
            text = {"N","I/YR","PV","PMT","FV","P/YR"}
        }
    
        counties.callback = function(txt) countyName:inputString(txt) var=txt counties:hide() end
    
        Soda.DropdownButton{
            parent = panel1, style = Soda.style.default, x = -10, y = -30,
            shapeArgs = {corners = 4 | 8},
            callback = function() counties:toggle() end
        }
    
    end
    
    
  • Posts: 257

    hello,

    i manage to extract information in soda.list

    counties.callback = function(txt) countyName:inputString(txt) var=txt counties:hide() end

    but how extract tha value in soda.textentry

    i tried the callback function in soda.textentry but it doesn't work

    can you help me please

  • Posts: 1,972

    @hpsoft thanks for trying Soda and for posting your code. I haven't had a chance to run your code yet, but I will do tomorrow. TextEntry currently has an output method TextEntry:output() which returns the value in the textbox. I can see you have a lot of text entry fields so perhaps this method isn't convenient for you. I could implement a callback for TextEntry if that would be useful (it would be consistent with the interface for lists). Should the callback trigger on each key press, or just when the user hits return/ closes the keyboard? Which would be most useful do you think?

  • (this is so impressive)

  • Posts: 257

    if i replace code by

    Soda.TextEntry { parent=panel1, x=10, y=-30, w=400, h=40, title = "N = Nombre de paiements =  ", default = "0", callback = function()   N=TextEntry:output()   end }
    

    N is empty, i don't understand

  • Posts: 1,972

    @hpsoft TextEntry doesn't have a callback (yet). I'm going to add it. I've decided that the most convenient way to implement this is if the callback is triggered when the user exits the textbox, either by pressing return, pressing hide keyboard, or selecting another button (which hides the keyboard). I'll put the updated code on GitHub in the next day or so.

  • Posts: 257

    great thank you
    I will end my example...

    and i will add 2 horizontal menus
    and try to add sound effects ( like laser ) when panel appear or desappear
    perhaps with waveform

  • Posts: 257
    function equations2()
    
        var="FV"
    
        local panel = Soda.Control { 
            title = "Calculus", hidden = true, x=50, y=50, w=900, h=700, 
            blurred = true, style = Soda.style.darkBlurred
        }
    
        local menu = Soda.MenuButton {x = -20, y = -20}
        menu.callback = function() panel:show(RIGHT) menu:hide(RIGHT) end 
    
        Soda.BackButton {
            parent = panel, direction = RIGHT, x = -20, y = -20,
            callback = function() panel:hide(RIGHT) menu:show(RIGHT) end
        }
    
        local finpanel = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local mathpanel = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local statpanel = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local physpanel = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local otherpanel = Soda.Frame{
            parent = panel, hidden = true, x = 20, y = 20, w = -20, h = -140,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        Soda.Segment{
            parent = panel, x = 20, y = -80, w = -20, h = 40,
            text = {"Finance","Math","Stat Proba","Chemical Physics","Other"},
            panels = { finpanel, mathpanel , statpanel, physpanel, otherpanel}
        }
    
        local tvmpanel = Soda.Frame{
            parent = finpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local cfpanel = Soda.Frame{
            parent = finpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local bondpanel = Soda.Frame{
            parent = finpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local deprecpanel = Soda.Frame{
            parent = finpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        Soda.Segment{
            parent = finpanel, x = 20, y = -10, w = -20, h = 40,
            text = {"TVM","Cash Flow","Bond","Depreciation"},
            panels = { tvmpanel, cfpanel, bondpanel, deprecpanel
            }
        }
    
        local solverpanel = Soda.Frame{
            parent = mathpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local eq2ndpanel = Soda.Frame{
            parent = mathpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local eq3ndpanel = Soda.Frame{
            parent = mathpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local convertpanel = Soda.Frame{
            parent = mathpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local systempanel = Soda.Frame{
            parent = mathpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        local graphpanel = Soda.Frame{
            parent = mathpanel, hidden = true, x = 20, y = 20, w = -20, h = -80,
            shape = Soda.RoundedRectangle, style = Soda.style.translucent,
        }
    
        Soda.Segment{
            parent = mathpanel, x = 20, y = -10, w = -20, h = 40,
            text = {"Solver","Eq2nd","Eq3nd","Convert","System","Graph"},
            panels = { solverpanel, eq2ndpanel, eq3ndpanel, convertpanel, systempanel , graphpanel
            }
        }
    
        Soda.TextEntry { parent=tvmpanel, x=10, y=-30, w=0.5, h=40, title = "N =  ", default = "0"}
        Soda.TextEntry { parent=tvmpanel, x=10, y=-80, w=0.5, h=40, title = "I/YR = ", default = "0"}
        Soda.TextEntry { parent=tvmpanel, x=10, y=-130, w=0.5, h=40, title = "PV = ", default = "0"}
        Soda.TextEntry { parent=tvmpanel, x=10, y=-180, w=0.5, h=40, title = "PMT = ", default = "0"}
        Soda.TextEntry { parent=tvmpanel, x=10, y=-230, w=0.5, h=40, title = "FV = ", default = "0"}
        Soda.TextEntry { parent=tvmpanel, x=10, y=-280, w=0.5, h=40, title = "P/YR = ", default = "12"}
        Soda.Switch{ parent=tvmpanel, x = 10, y = -330, title = "Début / Fin" }
    
        Soda.Button{
        parent = tvmpanel, title = "Compute", style = Soda.style.warning, 
        x = -100, y = 20, w = 200, h = 40, 
        callback = 
            function()
                if var=="PMT" then end
                Soda.Alert1{ 
                    title = var.." = ", y=0.6, 
                    style = Soda.style.darkBlurred, blurred = true, 
                    alert = true
                }
             end
        }
    
        local tvmVar = Soda.TextEntry{
            parent = tvmpanel, x = 430, y = -30, w = 340, h = 40, shapeArgs = {corners = 1 | 2},
            title = "Compute : ", default = "FV", inactive = true
        }
    
        local tvm = Soda.List{
            parent = tvmpanel, hidden = true, x = 430, y = -70, w = 380, h = 240,
            text = {"N","I/YR","PV","PMT","FV","P/YR"}
        }
    
        tvm.callback = function(txt) tvmVar:inputString(txt) var=txt tvm:hide() end
    
        Soda.DropdownButton{
            parent = tvmpanel, style = Soda.style.default, x = -10, y = -30,
            shapeArgs = {corners = 4 | 8}, callback = function() tvm:toggle() end
        }
    
    
  • Posts: 257
    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.TextEntry { parent=bondpanel, x=10, y=-30, w=0.5, h=40, title = "SDT = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-80, w=0.5, h=40, title = "CPN = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-130, w=0.5, h=40, title = "RDT = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-180, w=0.5, h=40, title = "RV = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-230, w=0.5, h=40, title = "ACT = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-280, w=0.5, h=40, title = "YLD = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-330, w=0.5, h=40, title = "PRI = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=10, y=-380, w=0.5, h=40, title = "AI = ", default = "0"}
        Soda.TextEntry { parent=bondpanel, x=430, y=-30, w=380, h=40, title = "DUR = ", default = "0"}
    
        Soda.TextEntry { parent=deprecpanel, x=10, y=-30, w=0.5, h=40, title = "SL = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-80, w=0.5, h=40, title = "SYD = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-130, w=0.5, h=40, title = "DB = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-180, w=0.5, h=40, title = "DBX = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-230, w=0.5, h=40, title = "SLF = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-280, w=0.5, h=40, title = "DBF = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-330, w=0.5, h=40, title = "LIF = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=10, y=-380, w=0.5, h=40, title = "MO1 = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-30, w=380, h=40, title = "DT1 = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-80, w=380, h=40, title = "CST = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-130, w=380, h=40, title = "SAL = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-180, w=380, h=40, title = "YR = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-230, w=380, h=40, title = "DEP = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-280, w=380, h=40, title = "RBV = ", default = "0"}
        Soda.TextEntry { parent=deprecpanel, x=430, y=-330, w=380, h=40, title = "RDV = ", default = "0"}
    
        Soda.TextEntry { parent=solverpanel, x=10, y=-30, w=-10, h=40, title = "Equation = "}
    
        Soda.Button{
        parent = solverpanel, title = "Compute", style = Soda.style.warning, 
        x = -100, y = 20, w = 200, h = 40, 
        callback = 
            function()
                Soda.Alert1{ 
                    title = var.." = ", y=0.6, 
                    style = Soda.style.darkBlurred, blurred = true, 
                    alert = true
                }
            end
        }
    
        local solverVar = Soda.TextEntry{
            parent = solverpanel, x = 430, y = -80, w = 340, h = 40, shapeArgs = {corners = 1 | 2},
            title = "Algo : ", default = "Dichotomy", inactive = true
        }
    
        local solver = Soda.List{
            parent = solverpanel, hidden = true, x = 430, y = -120, w = 380, h = 240,
            text = {"Dichotomy","Newton-Raphson","Intersecting","Regula Falsi","Müller Method","IQI","Brent Method","Conjugate Gradient","Fixed Point Method","Sturm Theorem"}
        }
    
        solver.callback = function(txt) solverVar:inputString(txt) var=txt solver:hide() end
    
        Soda.DropdownButton{
            parent = solverpanel, style = Soda.style.default, x = -10, y = -80,
            shapeArgs = {corners = 4 | 8}, callback = function() solver:toggle() end
        }
    
        Soda.TextEntry { parent=eq2ndpanel, x=10, y=-30, w=150, h=40, title = "a^2+b^2+c=0"}
        Soda.TextEntry { parent=eq2ndpanel, x=10, y=-80, w=0.5, h=40, title = "a = ", default = "0"}
        Soda.TextEntry { parent=eq2ndpanel, x=10, y=-130, w=0.5, h=40, title = "b = ", default = "0"}
        Soda.TextEntry { parent=eq2ndpanel, x=10, y=-180, w=0.5, h=40, title = "c = ", default = "0"}
    
        Soda.TextEntry { parent=eq3ndpanel, x=10, y=-30, w=200, h=40, title = "a^2+b^2+c^2+d=0"}
        Soda.TextEntry { parent=eq3ndpanel, x=10, y=-80, w=0.5, h=40, title = "a = ", default = "0"}
        Soda.TextEntry { parent=eq3ndpanel, x=10, y=-130, w=0.5, h=40, title = "b = ", default = "0"}
        Soda.TextEntry { parent=eq3ndpanel, x=10, y=-180, w=0.5, h=40, title = "c = ", default = "0"}
        Soda.TextEntry { parent=eq3ndpanel, x=10, y=-230, w=0.5, h=40, title = "d = ", default = "0"}
    
        Soda.TextEntry { parent=convertpanel, x=10, y=-30, w=-10, h=40, title = "Value = "}
    
        Soda.Button{
        parent = convertpanel, title = "Convert", style = Soda.style.warning, 
        x = -100, y = 20, w = 200, h = 40, 
        callback = 
            function()
                Soda.Alert1{ 
                    title = var.." = ", y=0.6, 
                    style = Soda.style.darkBlurred, blurred = true, 
                    alert = true
                }
            end
        }
    
        local convertVar = Soda.TextEntry{
            parent = convertpanel, x = 430, y = -80, w = 340, h = 40, shapeArgs = {corners = 1 | 2},
            title = "Conversion : ", default = "Base", inactive = true
        }
    
        local convert = Soda.List{
            parent = convertpanel, hidden = true, x = 430, y = -120, w = 380, h = 160,
            text = {"Base","Morse","Roman","Unit"}
        }
    
        solver.callback = function(txt) convertVar:inputString(txt) var=txt convert:hide() end
    
        Soda.DropdownButton{
            parent = convertpanel, style = Soda.style.default, x = -10, y = -80,
            shapeArgs = {corners = 4 | 8}, callback = function() convert:toggle() end
        }
    
        Soda.TextEntry { parent=systempanel, x=10, y=-30, w=0.5, h=40, title = "Numbers of unknowns = ", default = "0"}
    
        Soda.TextEntry { parent=graphpanel, x=10, y=-30, w=800, h=40, title = "f(x) = "}
        Soda.TextEntry { parent=graphpanel, x=10, y=-80, w=0.5, h=40, title = "Xmin = "}
        Soda.TextEntry { parent=graphpanel, x=10, y=-130, w=0.5, h=40, title = "Xmax = "}
        Soda.TextEntry { parent=graphpanel, x=10, y=-180, w=0.5, h=40, title = "Xstep = "}
        Soda.TextEntry { parent=graphpanel, x=430, y=-80, w=380, h=40, title = "Ymin = "}
        Soda.TextEntry { parent=graphpanel, x=430, y=-130, w=380, h=40, title = "Ymax = "}
        Soda.TextEntry { parent=graphpanel, x=430, y=-180, w=380, h=40, title = "Ystep= "}
    
        Soda.TextEntry { parent=statpanel, x=10, y=-30, w=-10, h=40, title = "X = ", default = "{  }"}
        Soda.TextEntry { parent=statpanel, x=10, y=-80, w=-10, h=40, title = "Y = ", default = "{  }"}
    
        Soda.TextEntry { parent=physpanel, x=10, y=-30, w=-10, h=40, title = "Balance Equation = "}
    
    end
    
    
  • Posts: 257

    here is the second version of example ( in 2 parts , sorry for the long text )
    it s very fantastic !!! simultaneus menubars works perfectly and very fast.
    I have a small problem ( i had to replace w=0.5 or w=1 by w=380 and w=800 )
    if not exceeded the fields of the panel. It s a very good work ^:)^

  • Posts: 257

    @yojimbo2000 my second most anboying problem that i do not manage to solve ( excuse my poor english )

    my first dropdown button ( in tvm pannel works perfectly )
    but the other dropdown button ( in solver panel and convertpanel ) does'nt work
    perhaps is there an order i've missed or a problem with text variable

    Thanks

  • edited September 2015 Posts: 1,972

    @hpsoft that's quite an impressive interface you've built! There's one error in your code producing the drop down bug (but also one error in my code). On line 256, you define the convert callback with solver, overwriting the previous solver callback. Change the start of the line to convert.callback =.

    However, this also brought to light a bug in Soda code. If you activate the convert and solver drop downs, but then switch back to the first dropdown, you'll notice that the image of the second menu is displayed in place of the first one. This is a bug to do with how roundedRect caches shapes. Because two of the menus are the same dimensions, roundedRect caches them as a single mesh (what it's supposed to do), but then doesn't switch the textures when you switch between them. I'll have a fix for this bug, and quite a few new features later today. Thank you for bringing this bug to light!

  • edited September 2015 Posts: 1,972

    Soda v0.2 is now available at the GitHub link at the top of the post.

    It now has fairly comprehensive hyperlinked documentation, in the GitHub readme (in the demo, you can access this page by pressing the query button in the corner of the pane)

    Here are the release notes:

    Version Notes

    v0.2

    • NEW Soda.DropdownList - A button which, when pressed, toggles a dropdown list (this is a wrapper or factory which makes it much easier to setup dropdown lists). When an item is selected from the list, the button's label changes to reflect the selection, and an optional callback is triggered.

    • NEW Soda.Toggle - now, in addition to iOS-style Soda.Switch (with an animated lever that ficks back and forth), any button can behave as a toggle. This has been implemented by separating the graphics and animation of Soda.Switch from the toggle button logic.

    • Callbacks have been made more consistent and are triggered by more elements. Callbacks are now always triggered as self:callback (with a colon) so the first argument passed to the callback will always be the sender's self. If you have any callbacks that take an argument, eg callback = function(inkey), these will now need to have a self/this variable as their first argument (how you name the variables passed to the callback is up to you): eg callback = function(self, inkey).

      • In Soda.TextEntry, callback is triggered by hitting return or the close keyboard button (but not by selecting a different interface element, which closes the keyboard and cancels text entry). Callback is passed the string entered.

      • In Soda.List, callbacks return 3 variables: 1) the sender (the list object itself), 2) the selected item, 3) the selected item's title string

      • Soda.Toggle and Soda.Switch have two callbacks: callback (when on state is activated) and callbackOff

    • new update parameter. Like a callback, but triggered every frame, in case any elemenets need constant updating (see the new profiler panel in the demo).

    • If you're using iOS 9, TextEntry fields now have cut, copy, and paste ;-)

    • The Soda.Control wrapper is now called Soda.Window

    • The drawing function called in draw must now be called Soda.drawing

  • edited September 2015 Posts: 1,972

    @hpsoft

    If you install the new version of Soda, you can use the new dropdownList entity. Instead of needing 3 elements to create a dropdown list, you can do it in one, like this:

        Soda.DropdownList{
            parent = tvmpanel, x = 430, y = -30, w = -10, h = 40, --w = 340
            title = "Compute",
            text = {"N","I/YR","PV","PMT","FV","P/YR"},
            defaultNo = 5
        }
    
    ...
    
        Soda.DropdownList{
            parent = solverpanel, x = 430, y = -80, w = -10, h = 40,
            title = "Algo",
            text = {"Dichotomy","Newton-Raphson","Intersecting","Regula Falsi","Müller Method","IQI","Brent Method","Conjugate Gradient","Fixed Point Method","Sturm Theorem"},
            defaultNo = 1
        }
    
    ...
    
        Soda.DropdownList{
            parent = convertpanel, x = 430, y = -80, w = -10, h = 40,
            title = "Conversion",
            text = {"Base","Morse","Roman","Unit"},
            defaultNo = 1
        }
    
  • RcvRcv
    Posts: 5

    @yojimbo200

    great job you've done, thanks for sharing.

    I tried your new version, and I think there is a problem with list, I think not selected properly, you have to touch several times to select it.

    Sorry for my bad english.

  • Posts: 1,972

    @Rcv yes, there is a bug with List still, where sometimes it returns the wrong answer. It's proving to be a tricky one to track down to be honest, because I can't work out the circumstances in which it fails. Maybe 1 in 10 times it returns the wrong answer. I'll keep looking at it.

  • Posts: 1,972

    @Rcv OK, I'm pretty sure I've now zapped the bug where vertical lists occasionally return the wrong result. Let me know if you still get the bug. Updated code and installer at the github link.

  • Posts: 1,972

    Version 1 of the tutorial is now in the GitHub repository. It's a separate program, SodaTutorial.lua. Currently it just has 7 steps, taking you through very basic stuff, but many more steps will be added. Here's a screenshot:

    tut

  • edited September 2015 Posts: 257

    I have not yet tried my example with the new simplified controls
    But i have tried soda 0.2 and tutorialsoda 0.2 + dependencies ( with last updates ) functioning very good.

    The only problem is always with list. The result is not wrong but i have to type repeatedly my choice ( up to 6 times ) for it to take effect. I have iOS 8.3 and 1GB RAM remaining. Perhaps with iOS 9 it's work.

    If it helps, the problem is more present on the tutorial. ( up to 8 times )

    Thanks for your big big work

  • amazing and cool effect,

  • Jmv38Jmv38 Mod
    edited September 2015 Posts: 3,265

    @Yojimbo2000 here is the correction for @hpsoft bug report:
    in Scroll, line 45, replace

            if self.touchMove==0 then --only test selectors if this touch was not a scroll gesture
    

    by

            if math.abs(self.touchMove)<10 then --only test selectors if this touch was not a scroll gesture
    

    then it works as expected. The problem is that you always move your finger by a couple pixels between BEGAN and ENDED, so demanding 0 is too much.

Sign In or Register to comment.