Question about UI design and cloning

I’m designing our internal tools for UI creation.

The basic design question I have is:

  • it would make a lot of sense if all the low level components (i.e. button, label, etc.) and high level components (ie MENU etc.) were in some form clones of master components.

to achieve this, I would have to feed them tables with certain parameters through chop->In chops (since Selecting tables wouldn’t work as they’re all clones), then have the various parameters look at the incoming cells for settings.

I’m afraid though that this will create a lot of useless cooking first, then have way too many tab(*) functions happening.

  • the alternative is to design a system for copying individual components and work with scripts to set actual values instead of tab(…) expressions.

Obviously the alternative would be much faster, but much, much more complicated in terms of management.

My goal is of course to have the fastest UI possible, to be able to populate it with what I’m sure will turn out to be a lot of buttons/labels, sliders, etc. - so it seems I’d have to go with copying, but before I get too deep into it I wanted to ask the forum for any advice, comments, etc.

A third alternative, not yet available, would be to allow changing fields inside clones, so that when a master is updated, everything but the fields that are set differently from the default would get updated (argh, that would create the same problem as in Houdini where if you LIKE the default value and need it to be that way at the top level, if the DA changes anything that’s default gets changed as well, possibly creating problems) - as an alternative - more user oriented but probably easier also software wise, would be to allow the user to “mark” fields of clones as non-updateable by their masters.

Comments?
d

Well, you can use Select DATs to select the Table DAT that contains the per-clone parameter using various commands/expressions like opname() and opdigits() to build the path to the Table DAT. That will allow you to select different tables for different clones based on their names or location in the file. That may not be any easier than just using an In CHOP though. In the end In CHOPs and tab() command are really cheap, they shouldn’t be avoided. So I think the first way you listed is the way you should go. Mixxa is completely full of tab() commands, and they aren’t an issue. I wouldn’t spend time trying to avoid them, you won’t gain enough.

mmmm… mmmm…

is the tab() command optimized to the point where it’s cooked only when the source changes?

the plan I have will display/hide menus a lot, and what I don’t want is tab() cooking hits as I display 50 buttons from one frame to another for example.

d

Well, the tab() expression is called when the node cooks.
A node can cook for many reasons (refer to [url]http://www.derivativeinc.com/wiki/index.php?title=Cook[/url] for a description).

If the source table hasn’t changed, and there are no other reasons the node needs to cook, it won’t cook, so tab() won’t be called.

If the playback does hitch, it’s unlikely it was due to the tab() commands in particular anyway.

Having said that, we are using tab() heavily for some internal UI building, so if it turns out they are slow, you can expect them to be sped up in the future.

another question.

say I use a table and the tab expression.

right now I do have tables looking like:

panel_pos 2 mypanel/panelx 5 mypanel/panely 10

I would like to use these kind of tables because it gives me info about what parameters that UI widget accepts (i.e. a script may set panel_pos 30 40 therefore replacing the default 5 and 10 - the #2 signifies how many value that parameter expects).

It also allows a widget configuration menu to be created automatically by looking at the widget’s parm table.

so: mypanel/panelx would have a formula like: tabr(configtableorothername,“panel_pos”,3)

this is all very nice and clean, but what if I want to have panelx be driven by some channel or other widget’s parameter?
the also very nice and clean way would be for me to set that cell, instead of its default of 5, to something like: ch(“somepath/somechan”)

I’ve tried this and even passing through the evaluate dat it doesn’t expand correctly (and besides the evaluate dat also changes my other cell mypanel/panelx to 0) no matter what I tried.

Am I missing something?
d

Also, I think Achim mentioned - in relation to /local/variables, that changing a cell will cook all dependencies for the table, not just that cell.

so maybe I should do a table for each field? urgh, that would be thousands of tables. Any plan to cook according to cell changes instead of table changes?

d

I did it in a similar way, but since the introduction of local variables, you can simplify it like this:

Create a UIDef table which has 1 col per UI gadget, and each row defines the gadget’s properties.
Then, to create the UI, you loop over the table, loadcomponent the appropriate tox and name it to match the col name
Inside each UI gadget, in the local container, I have a select DAT named variables, which selects the UIDef col matching $OPN
Each gadget can then use the properties as local variables, so no need for tab() stuff. Might not be faster, but it’s a cleaner layout when UIs get more complicated.

I agree, this would not only be necessary for local variables, but generally very useful.

Yes I’m using a table too, but I think I’m going to stick with copying its defaults to the fields, instead of using tab() expressions.

Things is, I really don’t like a couple of things:

1 - cloning requires the select to figure out the names of the tables. It’s not that hard for single clones, but in my UI I have clones inside clones inside clones inside clones… so I need a complex mechanism - if possible - to keep tables outside of the top level clones, or have them trickle in. A clone could be anywhere, at the top level or deep inside another series of clones.

2 - hooking up channels to tables makes it hard to animate their parameters on the fly, or set ch() or chop() expressions, and again, even if the table is only for that component, gets into too much cooking.

I really want the fastest and most flexible system.

Well, at the very least you’d have one table per node. One per field is overkill and won’t gain you anything over one table per node.

But as long as you don’t have one huge megatable, but instead maybe one table per component with some number of nodes in them, you won’t see any performance impact. Remember a node will only cook if someone is interested in it’s data (so it needs to visible or used as an input to some other node). So if you change a table and the node cooks, odds are that node was going to cook anyway since it was visible in the first place. If you change a table but the node isn’t visible and isn’t used anywhere, it won’t cook until it gets used (0 impact)

How about having the ability to put a ch() expression into a table, that will essentially get copied into the node’s parameter, and then evaluated (as if you had typed it in there)?

re: putting ch expressions (and expressions in general I’d think) inside cells to feed to node parms.

I think that would be good.

But I’m still weary of using “live” tables to feed into nodes. I’ve got a simple system (3 scripts, no longer than 30 lines each) to actually avoid cloning and set parms directly into the channels parameters, as opposed to set them into tables.

So I copy a widget (widgets have their own 3-4 lines of script to update themselves with other eventual subwidgets, before being copied), from my templates to the destination.

I then use the widgets’ MAPPER table to set all the widgets parameters (the public ones) to their defaults (since this happens for any widgets any subwidget gets treated the same way).

at that point I could:

g_ui_set mywidget/LABEL pos.y ‘ch(“mycrazyuserchanell”)’

where mywidget is an abstraction (ie. a menu) and LABEL is one of its subwidgets, and pos.y is a parm I defined as public which feeds into whatever the widget developer wants (all in a MAPPER table).

this seems to allow me a lot of flexibility.

Avoiding cloning allows any widget to slightly customize its subwidgets without losing the reference to the original (since it gets copied over everytime it’s needed).
Avoiding cloning is also good to avoid the nightmare of figuring out which table to select inside a clone. I.e. the LABEL widget could be a clone by itself, or could be a clone inside a clone inside a clone inside a clone. Pretty tough to manage those tables, if possible (i.e. you can’t rename it uniquely if its inside other clones, you have to feedit the name of a table, mantain hierarchies of tables elsewhere outside of the UI, etc. etc - a lot of trouble!

Avoiding live feeding of values through tables allows me of course to not have to recook the table and its dependents. If I set a button in a menu to feed off a channel, that path alone will be recooked, not the paths depending on the table (which may affect 20-30 places in the widget) once the table is recooked because one of its channel inputs has changed.
Also of course there’s a problem of funneling data through a cell, data which may be coming from all kinds of different places, user defined.

As you know, I’ve been working with Touch for years now, and on every single project we’re ending with months of optimizations (actually, you are :slight_smile:). The fact that we hit the real time wall on every project means that every microsecond counts. If I have a fast UI, that’s general enough to be used on any project, and only takes up the memory needed by that project (on that note it’s all dynamically loaded, icons and all, depending on the need, as it’s created. The source networks can then be deleted and the only thing you are left with is only what you need/want)… that will be one less worry when it comes down to optimization.

d

Don’t get me wrong, there is nothing wrong with what you want do to. If you are comfortable doing it that way there is nothing wrong with it at all. I just didn’t want you to feel forced into doing it that way due a perceived issue with the tab() expression and cooking caused by updates to table.

On this topic, I just did some tests and what you can do is surround your tab expression with an eval(), and whatever is in the table will get evaluated. It can be a number, or something like a par() or chop() expression.
So you get that flexibility already without any software changes.

Attached is an example, the R and G and B channels from the Constant TOP are controlled by the 3 entries in the table. All with the same expression (except for the cell entry ofcourse)
tabexample.2.toe (1.9 KB)

Hmm, Dani, curious - did you look thru recent uig.tox… I’m using clones galore, usually pretty clean. And then there’s the “strip” gadgets in there - strips of components that are driven by tables, one component is created per table row, dynamically resized. This all may be elementary w.r.t. where you are at now though, but it may have relevant approaches.