Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NodeGraphEditor FieldEnum with dynamic options #15

Closed
rff255 opened this issue Apr 12, 2017 · 9 comments
Closed

NodeGraphEditor FieldEnum with dynamic options #15

rff255 opened this issue Apr 12, 2017 · 9 comments

Comments

@rff255
Copy link

rff255 commented Apr 12, 2017

Hello!
I'm planning use your NodeGraphEditor in my graduation work as a part of a cellular automata GUI I'm developing. First of all, thanks for open your code, it's a nice and versatile 'widget'.
I'm still a noob in imgui, so maybe I haven't see the solution to this, but seems like the function (passed in addFieldEnum()) that returns the name of a selected index, must be static (as your APPLE/ LEMON/ ORANGE example). Once my application allows the user to define in execution time some objects that will appear as options in a FieldEnum, I can't define it statically.

So, there is a way to do this? Would be great if I can use your NodeGraphEditor on my work :)

@Flix01
Copy link
Owner

Flix01 commented Apr 13, 2017

Well, as far as I can remember there should be a user pointer you can pass to the function.

So basically, based on it you could change some objects at execution time (or you can add a single callback to multiple combos for example).

A potential difficulty is that the number of items must be specified in addFieldEnum() and not in the callback...

Well, you can try and see if the user pointer is good for you and then report back.

@Flix01
Copy link
Owner

Flix01 commented Apr 13, 2017

On a closer look, I've just seen that addFieldEnum() mimics the main ImGui::Combo() method.
So now I can easily add the 2 overloads ImGui::Combo() has. This will end up in an API like this:

// addFieldEnum(...) now has all the 3 overloads ImGui::Combo(...) has.
// addFieldEnum(...) [1] (item_count + external callback)
node->fields.addFieldEnum(&node->enumIndex,3,&GetTextFromEnumIndex,"Fruit","Choose your favourite");
// addFieldEnum(...) [2] (items_count + item_names)
//static const char* FruitNames[3] = {"APPLE","LEMON","ORANGE"};
//node->fields.addFieldEnum(&node->enumIndex,3,FruitNames,"Fruit","Choose your favourite");
// addFieldEnum(...) [3] (zero_separated_item_names)
//node->fields.addFieldEnum(&node->enumIndex,"APPLE\0LEMON\0ORANGE\0\0","Fruit","Choose your favourite");

If this is OK for you I can add them in next commit.

[Edit:] committed.

@Flix01
Copy link
Owner

Flix01 commented Apr 14, 2017

@rff255 Have you solved it?
If not, can you please explain a bit more what you want to achieve?

What I cannot understand is if the change of your objects at execution time must be valid for all the instances of your node class, or only for some instances of the same node class.

  • In the first case it should be possible to do it more easily. You can try something like:
class ComplexNode : public Node {
    ...
   protected:
    static FieldInfo* pEnumFieldInfo;   // new
   ....
  public:
  static ThisClass* Create(const ImVec2& pos) {
  ...
  // 3) init fields ( this uses the node->fields variable; otherwise we should have overridden other virtual methods (to render and serialize) )
        node->fields.reserve(3);    // MANDATORY to avoid reallocation of the fields vector that caould invalidate pEnumFieldInfo
        // 3) init fields ( this uses the node->fields variable; otherwise we should have overridden other virtual methods (to render and serialize) )
        node->fields.addField(&node->Value[0],3,"Angles","Three floats that are stored in radiant units internally",2,0,360,NULL,true);
        node->fields.addFieldColor(&node->Color.x,true,"Color","color with alpha");
        // addFieldEnum(...) (item_count + external callback)
        pEnumFieldInfo = &node->fields.addFieldEnum(&node->enumIndex,3,&GetTextFromEnumIndex,"Fruit","Choose your favourite");
  ...
  }
   ....
};
FieldInfo* ComplexNode::pEnumFieldInfo = NULL;

Once you have pEnumFieldInfo, you can add to the class a public static method to change at runtime all the internal properties of the FieldInfo, that in your case are:

// used only for FT_ENUM (internally it uses FT_INT, pdata must point to an int):
        int numEnumElements;
        typedef bool (*TextFromEnumDelegate)(void*, int, const char**); // userData is the first param
        TextFromEnumDelegate  textFromEnumFunctionPointer;  // used only when type==FT_ENUM, otherwise set it to NULL. The method is used to convert an int to a char*.
        void* userData;          // passed to textFromEnumFunctionPointer when type==FT_ENUM (useful if you want to share the same TextFromEnumDelegate for multiple enums). Otherwise set it to NULL or use it as you like.        
  • In the second case, I guess that overriding the render method is the only solution, since the items of the enum the node must display are not the same for all instances, but depend on some node variables.
    The default render method works this way:
 // should return "true" if the node has been edited and 
// its values modified (to fire "edited callbacks")
 virtual bool Node::render(float nodeWidth)
    {
        bool nodeEdited = false;
        for (int i=0,isz=fields.size();i<isz;i++)   {
            FieldInfo& f = fields[i];
            nodeEdited|=f.render(nodeWidth);
        }
        return nodeEdited;
    }
  • Please note that all the code related to FieldInfo/FieldInfoVector is there just to provide automatic render code, copy and paste functionality and serialization (the latter only if imguihelper.h/cpp are used). Some users of the node graph editor don't need this serialization system, and don't use FiledInfo/FieldInfoVector at all: they just override the render method and are happy this way.

Hope I've clarified a lot of things correctly...

@rff255
Copy link
Author

rff255 commented Apr 14, 2017

@Flix01
Thanks for fast feedback, I'm glad you spend time with this. I had network problems since wednesday night and only now I can see and test your answers. I will try careful your suggestions and talk to you as soon as I can. But about your question at last comment: the list of objects will be read for all the instances.
i.e: the user can add or remove a new Attribute on another part of GUI, and the nodes of class GetAttributeNode will refresh the options they offer on FieldEnum.

@rff255
Copy link
Author

rff255 commented Apr 14, 2017

I'm confused:

  • first of all, ok: using the new overload addFieldEnum(), I could create a node and set the options of fieldEnum according to a variable (that contains the name options) definied in execution time. But if the options change (say, the user add another object that must be listed on fieldEnum) the node can't be refreshed because it receive a const variable. Is it true?

  • If so, I could still use your nodeGraphEditor, but every time the user change the list of objects whose names must be syncronized with the options available on node, I will be forced to clear and re-create the nodes to maintain coherence. I am right?

Considering true the two questions: to use the new overload addFieldEnum, I should change the ComplexNode::Create() function to receive arguments relative to my list of objects dinamically, right? and as consequence, I must change MyNodeFactory() to pass those extra arguments, right? and so on with all the sequence of calls, including the addNode() functions, until the NodeGraphEditor() constructor? or there is another shorter way?

Again, thanks for your attention, I hope I can include your work to enrich my(maybe ours) application :D

@Flix01
Copy link
Owner

Flix01 commented Apr 14, 2017

  • I think you should not use the 2 overloaded methods (as I told in my second post, they are simply derived from the first one, so they are less flexible). Sorry if this point was not clear enough.
    You can see the code of the overloaded methods to understand how they are implemented through the first one. It may be useful.

  • I think you should focus on the pEnumFieldInfo strategy. You don't need to clear anything, you just modify the existing FieldInfo of your enum at runtime.

So I think you're heading towards a wrong direction...

@Flix01
Copy link
Owner

Flix01 commented Apr 15, 2017

I've added a new commit with another addFieldEnum() overloaded method that takes a callback that returns the number of item..

I've made a gist that explains very basically how to do it (https://gist.github.com/Flix01/a7873b73f0ffb00c87260e5bf13a18d4).

However there is a problem I could not solve:
how to notify other nodes that the enum list have been changed ?

Please see the code for a better understanding of this (BIG) problem.

Flix01 added a commit that referenced this issue Apr 15, 2017
…ds. Solves #15. See: https://gist.github.com/Flix01/a7873b73f0ffb00c87260e5bf13a18d4

addons/imguihelper/imguihelper.h/cpp: Added an overloaded ImGuiHelper::Serializer::saveTextLines(...) to ease serialization of string lines vectors
@Flix01
Copy link
Owner

Flix01 commented Apr 15, 2017

OK. I solved the issue with a new commit that adds a Node Graph Editor reference to each Node.
Updated the code here: https://gist.github.com/Flix01/a7873b73f0ffb00c87260e5bf13a18d4 (now should work as expected).
dynamicenum

@Flix01 Flix01 closed this as completed Apr 15, 2017
@rff255
Copy link
Author

rff255 commented Apr 16, 2017

Wow, thanks @Flix01 !
I've tested this and work perfectly. Indeed, is more than I need 😄. Hope I can show you my work after three months.

Again, thanks for your attention to this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants