UX question about type selection

I’m trying to research how to use dynamic global channels and want to write my thoughts on UX


The first thing that strikes me when trying to work with GlobalChannel nodes is that it’s very difficult to set a type without creating something around it that is easy to configure. I really have a problem, I want to set a type, not create an object of that type to set it. What am I doing wrong? Is it a separate variable? Is it a pad? Readable serialisation can cause problems? I still can’t remember the keyboard shortcuts to create this. Ctrl+click, Alt+click? What’s the difference? This setup requires creating such an object, right-clicking on it and then setting the required property in the type field? Can we add a class selection after “generic” as well, at least?


I also can’t catch this moment, but sometimes such a variable is created with the editor open and I’m constantly freaking out about it. Definitely it’s created in this appearance by a pure double right-click scenario.

image


I’m not sure how to use it comfortably:

Is this the right way?

Again, it might be easier to set the type directly on the outputs/inputs?
Can this variable be a problem if I’m trying to serialise?

I struggle with the slots.
It definitely has a huge impact on serialisation.

btw

I realised that it is possible not to create channels in child objects and do this!

изображение

BUT

But the use of such an approach is quite limited

изображение

изображение

  • Global Channels: Please edit the type of the global channel in the Global Channel Browser and hit Save. This way you can configure a “Basket” Channel e.g. with the type IReadOnlyList<Fruit>.

    • This is cool because using and defining the channel are different things
    • You might want to have a patch ReplaceWithApples that always pushes a spread of three apples. From the usage and by only looking at this single patch you are not really able to infer the type of the channel. That’s why this is configured globally in the browser.
  • [ ] Type Annotation(s) on Node Applications. Makes total sense. I also miss this feature. Will bump that internally. Should be as easy as Configure → Type Argument 1, Type Argument 2. You’re completely right. We might not need it in the Global Channel case, but still.

  • [ ] Generic IOBox with editor: totally. It should only appear when creating an “Out-Box”. If you start a link on an input pin and create an IOBox upstream, the option “generic” doesn’t really make sense, as you are about to create an “In-Box”. This needs refinement.

  • Serialization: Subtype-Polymorphism is supported by our serializer. If the actual type of the object (Apple) (runtime type) is different from the compile-time type (your type annotation) then not only the Object and it’s properties get serialized, but also the type name gets stored in order to be able to deserialize later an object of the right type.

  • auto-lifting from T to Channel<T>: yes we offer this for some types. Even external libraries can have this (e.g. Fuse) just in order to get away with fewer nodes. But sure: it’s not as powerful as having access to the Channel… Maybe we were a bit quick with adding this auto-conversion? What do you think?

  • Clamp: Consider adding your own Channel Operator. Start by looking at GetSlice (Channel). You could define it as such: whenever one or the other channel pushes a value you only push further when the clamped value changes. You could do this with a merge that calls Clamp and EnsureValue in both moments.

1 Like

I will try to answer the serialisation question first.

If I want readable data after serialisation (e.g. for manual editing, transfer to other software over the network, exclusion of the entity “channel” from the exchange, I can think of many options), I have to give up the slots containing the channel within the patch.

If I give up the slots, I am left with few options for linking the interface to the data, and I lose all the flexibility of the amazingly powerful imgui.

I have isolated the problem and here is an illustration of it

ChannelsValuesSlotsSerialization.vl (30.3 KB)

It’s important to understand that I think it’s worth researching how people create boxes. I keep forgetting to start patching before I create something, I forget that there is an option to create something directly on the cord. I know it’s faster and maybe some design decisions are made because of it, I’ve seen Joreg patching. But I build the box first and then patch it, it’s like it’s more convenient for me to build and configure it first and then think about where to patch it. If I’m the only one, it doesn’t need to change that design, but how do other people do it? Maybe I have like-minded people among ordinary users, not developers.

I also have a strong feeling that it’s time to separate IOBoxes from other entities, such as internal variables. It’s as if we’ve reached a situation where the user interface is limiting and can lead to ambiguity. On the one hand, the IOBox is now an internal variable and rigidly bound to a type, but on the other hand, I cannot get control over internal variables without creating and using an IOBox. And that’s where the race for what’s first and what’s second begins.

It is likely that a solution has already been prepared and work is in progress, but I would like to share my thoughts.

I will try to answer the serialisation question first.

For me, it sounds like you are struggling to find the right way to save the state of your app.

A proven way of doing this is this:

  • invent a Model that is only for describing the state of your app. It doesn’t do anything. A model MyAppModel could have a Cells property of type Spread<CellModel>
  • when operating via the UI you only create a new model of the App and write it into an unconnected input pin via the node SetPinValue. VL will serialize the model for you and reload it. You don’t have to call Serialize!
  • Whenever the input changes you synchronize your living (mutating) app&cells to the changed description.

Doing it this way will enable you to implement undo/redo later on.
Most importantly it’s a proven concept and I think there are even workshops by Natan and Anton on the topic.

regarding the IOBox and Property issue:
Not really sure if I can follow. Create an operation SetFoo, which will write property Foo. This way you can write the two different values and attach two different IOBoxes making sure that Foo is different for the different instances.

Regarding UX: I also keep forgetting the shortcuts. You’re not alone. For me I start a link and then double click if I want to create something attached to the link. But, yeah, different workflows should work.

@gregsn I don’t seem to have the whole model application thing figured out yet. Let’s just say I don’t really understand how to do it right yet. Maybe I do, maybe not. I think this is too complex a concept for simple patching, this concept can be not applied in most cases, simplifying it.

I want to simplify the whole design process, and so far I feel like I’m bumping into things, not related to trying to take a shorter path, but to the UI telling me how to do things. Do you see what I mean?

I already have a “state” class and an object of that class. It may not be a model, but it’s easier for me, so I don’t have to create runtimes. The patch itself is already pretty good as a runtime. Now I have a problem connecting the values from this state of my application directly to the interface channels. Just read the above, or show me an example of how to make such a connection without complicating the patch. Serialization is just a side effect in this case, but the kind of side effect that gets in the way of the desired result. Can I somehow make a better example and continue the discussion?

I tried to illustrate the problem as best I could.

изображение

изображение

imgui_cells_practice.vl (46.8 KB)

imgui_cells_practice_channels_issue.vl (48.5 KB)

In short, I can’t figure out how to give the “cells” entities the functionality I want without affecting serialization, but also without making the concept more complicated. Now the problem is that the channel functionality affects serialization a lot. Maybe I’m doing something wrong (yes, most likely), or maybe I’ve stumbled upon some architectural problem and I’ve stated my thoughts on this above. What do you think?

with channel functionality, there would be no need to create a complex, expensive and dangerous model-application architecture. I can serialize my state object, I can manipulate it, I can save it. I can even do this with channels already, but the “channels” get in all the crevices, it becomes unreadable, impossible to pass to another application that knows nothing about channels.