Juce RAD


#1

OK, I have an idea that I’d like to do. I’ll just recap it here in case anyone feels keen or has something similar.

I’d like to re-purpose Jucer to work as a Rapid Application Development system. The idea is that along with designed the GUI, you would add lua code to each control, in a way similar to Visual Basic or RealBasic.

Some classes that show up in Jucer but not in the run GUI would have to be added, off the top of my head that would be:

Socket class
Timer class

Then also some essential classes would be needed:
Global system object, for time, audio I/O, screen sizes etc.
File access

The IDE would make a file combining the lua code and GUI definition that could be opened in ‘Jucer’ or in a runtime. On Mac, a new ‘app’ could be made with a copy of the runtime and a packaged lua/GUI file, not sure about other platforms.

The upside is that the runtime could be compiled for anything juce can run on.

For me, this would let me make simple control apps at the same speed as RealBasic, but a juce look and feel and performance.

Bruce


#2

I’d love to help with something like this, but since I’ve been doing essentially what you’re describing for my day job. I’d probably upset my University’s copyright lawyers.

If the implementation you settle on is different enough from my own though, I can’t see any reason why I couldn’t help out.

This is honestly quite likely as:

a) I’ve picked an application design that is ‘incorrect[1]’ but should be easy enough for someone else to quickly understand should I happen to fall under a bus.

b) I’ve actually wrapped many components too, so my Lua script can create and manage UI directly. It seems that you’re suggesting that the UI be handled entirely by Jucer produced C++, and Lua only being used for the application logic. If so, that’s a very different animal, to my current code.

FWIW, I found that many of JUCE’s classes can be wrapped quite closely in Lua, with functions matching almost entirely to methods. This could be automated, therefore. Some components, TreeViews being one example, just don’t sit well as scripting language implementations though, and it becomes necessary to reinterpret them.

The only major hurdle when working with Lua is that you’re always stuck in static space, so it can be cumbersome to gain access to class instances.

There are also obvious limits on how much flexibility of UI component implementation is possible with Lua. It’s fast, but not that fast. :slight_smile:

[1] extreme abuse of singletons. :oops: I’m not proud, just pragmatic. :frowning:


#3

Cool. Is your project closed source then?

Although there’s some scope for complex apps, most RealBasic apps I’ve made, and I think most on the whole are pretty simple, and fall into the ‘when I click this, do this’ category. The next step from that tends to be ‘save these n steps, and when I click this button, do them all’

So I think lua only logic would work fine. Conversely, having a static UI, maybe except stuff like labels, position, visibility, should be fine, so the jucer file would work. In VB/RB, you can instantiate built-in classes like sockets and timers, or your own classes, but it occurs to me that the jucer conversion part of this project could be even more minimal if the lua code made them - essentially just a field for the lua scripts.

Then create a midi/audio/screen/socket connection and refer to it by index in lua, which I think you’re saying would be easier, something like:

onStart()
createSocket (1, “listen”, 3, 2001) // index 1, listen mode - 3 clients, port 2001
createSocket (2, “connect”, “192.168.0.5:2001”) // index 2, connect to

onButton (in one of the UI elements)
send (2, “Hello World”)

I’m mostly looking to do quick control apps. I suppose some sort of canvas object would be needed though, probably an exposed subclass of component that can be instantiated via jucer.

Bruce


#4

Yeah. My department is very leery of open source stuff, which is a shame. I suspect no-one would care if I released my app to the wider community for free (as in beer) say, but the University wants a tight control on the actual IP.

How would you envisage managing the availability of UI options, I.E. JUCE’s command manager, working within such a framework? Or wouldn’t you?

In my own script implementation I’ve fallen back on simple static event handlers (think JavaScript’s DOM event system), forcing script writers to deal with enabling and disabling controls based on state manually. As an approach this is appropriate enough for a scripting language, but clearly would not scale well to larger projects. In my case this is fine, as my users are taking a powerful and fully featured application, and merely tailoring it for specific tasks (typically observer studies, so most script concentrate on removing functionality as much as adding it).

I wouldn’t want to tackle a full sized application with this implementation though.

It seems to me that it could potentially be possible to use a Lua state table to interface with an ApplicationCommandManager, such that the C++ back-end can do all of the heavy lifting, though I’ve given this idea no prior consideration. Either way, I’d be interested to hear your thoughts (mostly 'cos they’ll help me better get a feel for the scope you are trying to define with this idea).

You certainly could do something like that. I’m put in mind of Delphi’s non UI components. If you’ve never used Delphi: you could add items to your canvas that weren’t actually UI elements, and they’d just sit there as icons (often in the way as it happens, but that’s life). Once added though, you could interact with them through the properties browser, just like any other component on your canvas. So a timer would show as a timer icon somewhere on your Form (canvas). In Lua, once named, that timer would show as a global of some kind, and could be accessible to any code. To flash a button then might simply be a case of:

- add a button to the canvas, and name it button1.
- add a timer to the canvas, and name it buttonFlashTimer.
- set its timer period.
- double click its onTimer() event which will create the Lua event function.
- add a few lines of Lua to the generated function.

function onButtonFlashTimer()
  if button1:getColour() == 0xFFFF0000 then
    button1:setColour(0xFFB0B0B0)
  else
    button1:setColour(0xFFFF0000)
  end
end

if that makes any sense?

Essentially, though I’d suggest using named instances not indexes. Lua is not much slower when using associative arrays than for numeric arrays.

My example above gives a hybrid approach, in that you can instantiate a socket in Lua, or directly from the Jucer as you’re doing here. I also use a tabled based class, but that’s purely stylistic.

Wrapping a component to create a painter (I’m guessing you’re thinking about waveform outputs and the like) is easy enough. JUCE’s Graphics model is total overkill for a scripting language, and would essentially be wedded into the component. Perhaps the easiest approach is simply to create a Lua wrapper around a consolidated image class (complete with read/write implementations), then allow your Canvas class to be given an Image instance which it would use to draw itself.

In this way, generate waveform would look something like:


function onMyButtonClicked()
  local image = Image.new(300, 200) -- always ARGB
  -- do some Lua code to update Image
  setCanvasImage(myCavas, image)
  -- the above could be rewritten as myCanvas:setImage(image)
end

Are we on the same track here?


#5

Sounds like the same page.

I was thinking that using a global index (in C++) of sockets etc would minimize the changes to jucer, although placing sockets and timers in jucer is OK too, and jucer would need to be modified to save differently and to make ‘canvas’ components anyway.

Dynamic sockets might be a bit more than I need generally, although incoming connections need to be dealt with too. Maybe not. Incoming data would have to come to a central point, in which case it would need at least a port it came from, or maybe a port and index. The lua would have to switch based on the port to decide what to do. Maybe a connection ID? It’s either that, or we have to create juce objects per socket connection, giving each a set of handlers and a state in lua? Sorry to harp on sockets, but it’s what I end up doing a lot.

As for the command manager stuff, it didn’t occur to me yet. Most simple simple apps don’t need that for physical buttons, only menu commands. If we had user commands that had handlers, they could just leave them enabled.

And then, it occurs to me, file handling is never quite right in RB/VB anyway - lots of duplication every project. How about if each app automatically opens and saves XML data, probably pushing it to and from a table. Then juce could manage saving & save as. There would be an onNew, onSave and onOpen hook.

I’m afraid my Lua isn’t currently up to State tables right now :? so I’m not sure how they fit in.

As for canvases, I was thinking more of semi-custom drawing, like gradients, paths etc. I see what you mean about lua being able to do it too. Maybe - if the user needs it, they can add the lua they need to draw what they need? In which case your image code would work really well.


#6

[quote=“Bruce Wheaton”]Sounds like the same page.

I was thinking that using a global index (in C++) of sockets etc would minimize the changes to jucer, although placing sockets and timers in jucer is OK too, and jucer would need to be modified to save differently and to make ‘canvas’ components anyway.

Dynamic sockets might be a bit more than I need generally, although incoming connections need to be dealt with too. Maybe not. Incoming data would have to come to a central point, in which case it would need at least a port it came from, or maybe a port and index. The lua would have to switch based on the port to decide what to do. Maybe a connection ID? It’s either that, or we have to create juce objects per socket connection, giving each a set of handlers and a state in lua? Sorry to harp on sockets, but it’s what I end up doing a lot.
[/quote]

I guess it depends on whether you anticipate multiple connections on a single port, or one to one communication. For my application, I wanted an instance to be able to act as a true server, and handle multiple clients on a single port. I had grand ideas of people using the system for a lightweight clustering system for heavy number crunching tasks to be shared across our desktop machines. Most of my users couldn’t care less that the app can be networked though, so the system has never really been put to the test.

Wrapping JUCE’s socket system to provide a full featured network library (in the Lua sense) is trivial though, so both options can be explored.

Yes, there’s definitely a complexity line that needs to be drawn.

That’s the essential question. Is the complexity of a state manager offset by the benefits? One clear benefit of a well crafted state manager is that a lot of Lua code would simply vanish into the C++ backend. Another is that the user interface would be dynamic largely for free. The third, as you suggest above, is that the implementation provides the essential framework for hassle free project management.

From a scripting language point of view, it’s a tempting proposition. The cost is extra code on the backend, and quite a number of changes to the Jucer.

The concept would be easy enough. Create a global table within Lua that is an associative array. Implement a metatable with _newindex support for trapping new values being added to the table. The _newindex implementation would have three functions:

  1. Limiting table values from being anything other than strings, bools, and numbers. Functions, and other tables would not be valid, so we need to stop the user from being daft.

  2. Add new properties to an internal C++ list. To save us trying to drill out values out of a lua associative array, we can capture new properties on the fly.

  3. Triggering event callbacks to the effect of “state has changed”

With this in place, bundling the whole lot up into an XML document would be no more effort than a for/next.

[quote]
As for canvases, I was thinking more of semi-custom drawing, like gradients, paths etc. I see what you mean about lua being able to do it too. Maybe - if the user needs it, they can add the lua they need to draw what they need? In which case your image code would work really well.[/quote]

Were you talking about drawing operations provided as part of the Jucer interface then? I was misunderstanding you.

With an image reference, or library, it is easy to give all the tools necessary to draw custom images to a Lua script. The performance can be a little hairy, unless some care is taken to optimize the internals of the image library. For most tasks its fine though. Custom drawing of components? Possibly. Offline rendering of waveforms, or other such outputs? Absolutely. On the fly animated wireframes. No[1].

[1] yes, I tried. :slight_smile:


#7

[quote]The concept would be easy enough. Create a global table within Lua that is an associative array. Implement a metatable with _newindex support for trapping new values being added to the table. The _newindex implementation would have three functions:

  1. Limiting table values from being anything other than strings, bools, and numbers. Functions, and other tables would not be valid, so we need to stop the user from being daft.

  2. Add new properties to an internal C++ list. To save us trying to drill out values out of a lua associative array, we can capture new properties on the fly.

  3. Triggering event callbacks to the effect of “state has changed”

With this in place, bundling the whole lot up into an XML document would be no more effort than a for/next. [/quote]

I have a trick used in a couple of apps that make it even easier. I just keep all my data in the XML. When something changes, it just merges into the XML. That gives a tree data structure without loads of gets, sets, and sub-objects etc.

We could either have the user store anything they want in a lua table, and then pass that back and forth - in which case they have to deal with get and set of UI elements, or else we use the trick haydxn did for me, where get and set have a path and value, then all the data is in the app.

Point for me being that every new sub-app has a built in document. Something like:

document.set (“pirates/galleons/black pig”, “scurvy”, true)
document.save ()

etc.

I’m sure it will be tempting to link UI right to/from data but that may push the project into too big a deal to start in on.

Bruce


#8

Would you prefer that to:

document.pirates.galleons.blackPig.scurvy = true

which is about as much effort to implement.

The only difference in usage is a little added verbosity, namely creating the levels the first time you need them:

document.pirates = document.new()
document.pirates.galleons = document.new()
document.pirates.galleons.blackPig = document.new()
document.pirates.galleons.blackPig.scurvy = true;

I could knock out the former in a morining, and the latter in about an hour more.

The advantage of this approach is that it allows your script to do things like:

if documents.pirates.galleons then
– we know the key exists so do something
end

or

if type(documents.pirates.galleons) == “table” then
– we know the key exists, and we know its type, so do something
end

For reference, document.new() is defined as:

function newDocument()
local t = {}
setmetable(t, documentMetatable)
return t
end

This function would be implemented in c, so the user would never see it. They’d just use the document.new() function to add a new tree level to their document.

It might be easier than you think, but yeah, that can wait until we’re ready.


#9

I think we’re looking at this from different sides. I’m saying that:

document.set (“pirates/galleons/black pig”, “scurvy”, true)

Would call a C++ function, say:

setDocument (String valPath, String valKey, String value);

and I have an XML technique that sets those values in an XmlElement. The values don’t have to be saved in Lua at all.

But that does make setting UI elements a bit tedious. I think you’re saying you can do it on the lua side, in which case, yeah the easy way would be better. I guess then you end up calling a C function which converts the Lua table into XML? That sort of makes sense to me - I hadn’t thought of there being built-in lua code, to be honest.

Bruce


#10

Sorry, I wasn’t being clear.

All of the heavy lifting there sh(w)ould be done in C++. I just tend to follow the Lua documentation idiom of describing Lua-c code in terms of the Lua it represents.

The whole document ‘library’ could be managed by custom C++ code that extends a JUCE::XmlElement. From the point of any Lua script, it’d work just like a native Lua table with comparable meta functions.

Really the only fiddly aspect of doing this is reference counting the tables (though I have code to do that written). Beyond that, it’s a question of whether the convenience of single string paths outweighs the flexibility of lua tables.


#11

Guys - you might be interested in knowing that im currently working on a REALbasic to C++ converter. This will be used to create C++ apps from REALbasic projects using JUCE as the GUI.

Basically i prototype plugins in REALbasic before converting the whole caboodle to C++ in order to built AU/VST plugins.

Im not intending to sell this due to support issues, but if anyone wants to make an offer or suggestions re collaboration let me know.