Last active
June 30, 2022 05:41
-
-
Save Flix01/a7873b73f0ffb00c87260e5bf13a18d4 to your computer and use it in GitHub Desktop.
Revisions
-
Flix01 revised this gist
Apr 22, 2017 . 1 changed file with 10 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -143,15 +143,15 @@ class CustomEnumEditorNode : public Node { inline static const ThisClass* Cast(const Node* n) {return Node::Cast<ThisClass>(n,TYPE);} }; class CustomEnumUserNode : public Node #ifndef NO_DYNAMIC_CAST , public virtual ITestEnum #endif //NO_DYNAMIC_CAST { protected: typedef Node Base; //Base Class typedef CustomEnumUserNode ThisClass; CustomEnumUserNode() : Base() {} static const int TYPE = TNT_CUSTOM_ENUM_USER_NODE; int selectedEnumIndex; // field @@ -228,11 +228,11 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th // Similiar lines must be repeated for every new Node class definition that uses the dynamic enum nge.getAllNodesOfType(ImGui::TNT_CUSTOM_ENUM_USER_NODE,&nodes); for (int i=0,iSz=nodes.size();i<iSz;i++) { ImGui::CustomEnumUserNode& n = *ImGui::CustomEnumUserNode::Cast(nodes[i]); int& selectedIndexEnum = n.getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } ImGui::CustomEnumUserNode* copiedNode = ImGui::CustomEnumUserNode::Cast(nge.getCopiedNode()); // This is the internal node kept for copy/paste operations: we can't forget to process it if (copiedNode) { int& selectedIndexEnum = copiedNode->getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong @@ -278,12 +278,12 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th // Similiar lines must be repeated for every new Node class definition that uses the dynamic enum nge.getAllNodesOfType(ImGui::TNT_CUSTOM_ENUM_USER_NODE,&nodes); for (int i=0,iSz=nodes.size();i<iSz;i++) { ImGui::CustomEnumUserNode& n = *ImGui::CustomEnumUserNode::Cast(nodes[i]); int& selectedIndexEnum = n.getSelectedItem(); if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } ImGui::CustomEnumUserNode* copiedNode = ImGui::CustomEnumUserNode::Cast(nge.getCopiedNode()); // This is the internal node kept for copy/paste operations: we can't forget to process it if (copiedNode) { int& selectedIndexEnum = copiedNode->getSelectedItem(); if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n @@ -304,10 +304,10 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th return nodeEdited; } static Node* TestNodeFactory(int nt,const ImVec2& pos,const NodeGraphEditor& /*nge*/) { switch (nt) { case TNT_CUSTOM_ENUM_EDITOR_NODE: return CustomEnumEditorNode::Create(pos); case TNT_CUSTOM_ENUM_USER_NODE: return CustomEnumUserNode::Create(pos); default: IM_ASSERT(true); // Missing node type creation return NULL; @@ -450,3 +450,4 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, return 0; } -
Flix01 revised this gist
Apr 18, 2017 . 1 changed file with 26 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,7 +9,8 @@ // // Added also some code to serialize/deserialize the enum names ("TestEnumNames") together // with the Node Graph Editor itself (to the same file). // // Fixed a possible copy/paste bug #include <imgui.h> // intellisense only #include <addons/imguinodegrapheditor/imguinodegrapheditor.h> // intellisense only @@ -208,7 +209,7 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th selectedEnumIndex = itemIndex; nodeEdited = true; //Now we must correct all the "selectedItem>=itemPlacement" in all the NodeGraphEditor ImGui::NodeGraphEditor& nge = getNodeGraphEditor(); // Actually if we use more than one Node Graph Editor with the same node types, the Dynamic Enum is the same, so we should process other editors as well... # ifndef NO_DYNAMIC_CAST for (int i=0,iSz=nge.getNumNodes();i<iSz;i++) { ITestEnum* n = dynamic_cast<ITestEnum*>(nge.getNode(i)); @@ -217,6 +218,11 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } } ITestEnum* copiedNode = dynamic_cast<ITestEnum*>(nge.getCopiedNode()); // This is the internal node kept for copy/paste operations: we can't forget to process it if (copiedNode) { int& selectedIndexEnum = copiedNode->getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } # else //NO_DYNAMIC_CAST ImVector<ImGui::Node*> nodes; // Similiar lines must be repeated for every new Node class definition that uses the dynamic enum @@ -226,7 +232,13 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th int& selectedIndexEnum = n.getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } ImGui::ColorEnumUserNode* copiedNode = ImGui::ColorEnumUserNode::Cast(nge.getCopiedNode()); // This is the internal node kept for copy/paste operations: we can't forget to process it if (copiedNode) { int& selectedIndexEnum = copiedNode->getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } # endif //NO_DYNAMIC_CAST mustFocusInputText=true; } } @@ -255,6 +267,12 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } } ITestEnum* copiedNode = dynamic_cast<ITestEnum*>(nge.getCopiedNode()); // This is the internal node kept for copy/paste operations: we can't forget to process it if (copiedNode) { int& selectedIndexEnum = copiedNode->getSelectedItem(); if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } # else //NO_DYNAMIC_CAST ImVector<ImGui::Node*> nodes; // Similiar lines must be repeated for every new Node class definition that uses the dynamic enum @@ -265,6 +283,12 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } ImGui::ColorEnumUserNode* copiedNode = ImGui::ColorEnumUserNode::Cast(nge.getCopiedNode()); // This is the internal node kept for copy/paste operations: we can't forget to process it if (copiedNode) { int& selectedIndexEnum = copiedNode->getSelectedItem(); if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } # endif //NO_DYNAMIC_CAST if (--selectedEnumIndex<0) selectedEnumIndex=0; } -
Flix01 revised this gist
Apr 17, 2017 . 1 changed file with 83 additions and 49 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,10 +7,8 @@ // And if you can use dynamic_cast<>() making new Node types that use it is easier (non-intrusive) // Otherwise you must modify the code of CustomEnumEditorNode::render(...) for every new user class you add. // // Added also some code to serialize/deserialize the enum names ("TestEnumNames") together // with the Node Graph Editor itself (to the same file). #include <imgui.h> // intellisense only @@ -88,15 +86,16 @@ class CustomEnumEditorNode : public Node { int selectedEnumIndex; // field char buf[MAX_ENUM_NAME_LENGTH]; bool mustFocusInputText; public: virtual const char* getTooltip() const {return "CustomEnumEditorNode tooltip.";} virtual const char* getInfo() const {return "CustomEnumEditorNode info.\n\nThis is supposed to display some info about this node.";} virtual void getDefaultTitleBarColors(ImU32& defaultTitleTextColorOut,ImU32& defaultTitleBgColorOut,float& defaultTitleBgColorGradientOut) const { // [Optional Override] customize Node Title Colors [default values: 0,0,-1.f => do not override == use default values from the Style()] defaultTitleTextColorOut = IM_COL32(220,220,220,255);defaultTitleBgColorOut = IM_COL32(0,75,0,255);defaultTitleBgColorGradientOut = -1.f; } // create: static ThisClass* Create(const ImVec2& pos) { @@ -110,16 +109,19 @@ class CustomEnumEditorNode : public Node { // 3) init fields ( this uses the node->fields variable; otherwise we should have overridden other virtual methods (to render and serialize) ) node->fields.addFieldEnum(&node->selectedEnumIndex,&CustomEnumEditorNode::GetNumEnumItems,&CustomEnumEditorNode::GetTextFromEnumIndex,"###CustomEnumEditor",NULL,&TestEnumNames); // Please node that in the render(...) method we assume that the dynamic enum FieldInfo is the first one (at node->fields[0]). // 4) set (or load) field values node->selectedEnumIndex = 0; // field value node->buf[0]='\0'; // other (non-field) variables node->mustFocusInputText = false; return node; } protected: virtual bool render(float nodeWidth); virtual bool canBeCopied() const {return false;} public: @@ -142,7 +144,7 @@ class CustomEnumEditorNode : public Node { }; class ColorEnumUserNode : public Node #ifndef NO_DYNAMIC_CAST , public virtual ITestEnum #endif //NO_DYNAMIC_CAST { protected: @@ -162,8 +164,7 @@ class ColorEnumUserNode : public Node public: int& getSelectedItem() {return selectedEnumIndex;} // ITestEnum if available, but used also if not available (to expose a private variable) // create: static ThisClass* Create(const ImVec2& pos) { @@ -179,7 +180,7 @@ class ColorEnumUserNode : public Node node->fields.addFieldEnum(&node->selectedEnumIndex,&CustomEnumEditorNode::GetNumEnumItems,&CustomEnumEditorNode::GetTextFromEnumIndex,"Selection","select your favourite",&TestEnumNames); // 4) set (or load) field values node->selectedEnumIndex = -1; return node; } @@ -193,11 +194,13 @@ class ColorEnumUserNode : public Node }; bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if the node has been edited and its values modified (to fire "edited callbacks") { bool nodeEdited = false; if (mustFocusInputText) ImGui::SetKeyboardFocusHere(0); if (ImGui::InputText("New item",buf,MAX_ENUM_NAME_LENGTH,ImGuiInputTextFlags_EnterReturnsTrue /*| ImGuiInputTextFlags_AutoSelectAll*/)) { mustFocusInputText=false; if (strlen(buf)>0) { const int itemIndex=TestEnumNamesInsert(buf); if (itemIndex>=0) { @@ -211,7 +214,7 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th ITestEnum* n = dynamic_cast<ITestEnum*>(nge.getNode(i)); if (n) { int& selectedIndexEnum = n->getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } } # else //NO_DYNAMIC_CAST @@ -220,17 +223,26 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th nge.getAllNodesOfType(ImGui::TNT_CUSTOM_ENUM_USER_NODE,&nodes); for (int i=0,iSz=nodes.size();i<iSz;i++) { ImGui::ColorEnumUserNode& n = *ImGui::ColorEnumUserNode::Cast(nodes[i]); int& selectedIndexEnum = n.getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; // otherwise node n selected index gets wrong } # endif //NO_DYNAMIC_CAST mustFocusInputText=true; } } } else mustFocusInputText=false; if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s","insert a new item"); // Here we render field 0 (without marking the "nodeEdited" flag) if (fields[0].render(nodeWidth)) mustFocusInputText=true; // We use "mustFocusInputText" to keep the InputText focused while switching enum if (ImGui::IsItemActive()) mustFocusInputText=true; // This seems to cover the case when we switch to the same item if (TestEnumNames.size()>0) { ImGui::SameLine(); if (ImGui::SmallButton("x") && TestEnumNamesDelete(selectedEnumIndex)) { nodeEdited = true; mustFocusInputText=true; //Now we must correct all the "selectedItem>=selectedEnumIndex" in all the NodeGraphEditor ImGui::NodeGraphEditor& nge = getNodeGraphEditor(); @@ -239,7 +251,8 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th ITestEnum* n = dynamic_cast<ITestEnum*>(nge.getNode(i)); if (n) { int& selectedIndexEnum = n->getSelectedItem(); if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } } # else //NO_DYNAMIC_CAST @@ -248,12 +261,22 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th nge.getAllNodesOfType(ImGui::TNT_CUSTOM_ENUM_USER_NODE,&nodes); for (int i=0,iSz=nodes.size();i<iSz;i++) { ImGui::ColorEnumUserNode& n = *ImGui::ColorEnumUserNode::Cast(nodes[i]); int& selectedIndexEnum = n.getSelectedItem(); if (selectedIndexEnum==selectedEnumIndex) selectedIndexEnum=-1; // we're deleting the selected item of node n if (selectedIndexEnum>selectedEnumIndex) --selectedIndexEnum; // otherwise node n selected index gets wrong } # endif //NO_DYNAMIC_CAST if (--selectedEnumIndex<0) selectedEnumIndex=0; } if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s","delete"); } // Now we can draw the other fields normally (no one in this demo) for (int i=1,isz=fields.size();i<isz;i++) { FieldInfo& f = fields[i]; nodeEdited|=f.render(nodeWidth); } return nodeEdited; } @@ -289,7 +312,11 @@ inline bool TestEnumNamesSave(const char* filename) { #ifndef NO_IMGUIHELPER_SERIALIZATION_LOAD static bool TestEnumNamesTypeLoadCallback(ImGuiHelper::FieldType ft,int numArrayElements,void* pValue,const char* name,void* userPtr) { TestEnumNamesType* vec = (TestEnumNamesType*) userPtr; if (strcmp(name,"num_text_line_items")==0) { vec->resize(*((int*)pValue)); for (int i=0;i<vec->size();i++) (*vec)[i][0]='\0'; if (vec->size()==0) return true; } else if (ft==ImGui::FT_TEXTLINE && strcmp(name,"text_line_items")==0) { IM_ASSERT(numArrayElements<vec->size()); strcpy(&(*vec)[numArrayElements][0],(char*)pValue); @@ -310,54 +337,61 @@ bool TestEnumNamesLoad(const char* filename) { #endif //NO_IMGUIHELPER_SERIALIZATION_LOAD #endif //NO_IMGUIHELPER_SERIALIZATION const char* NodeGraphEditorSavePath = "testEnumNamesNodeGraphEditor.nge"; ImGui::NodeGraphEditor nge; // Mandatory methods void InitGL() { if (nge.isInited()) { // This adds entries to the "add node" context menu nge.registerNodeTypes(ImGui::TestNodeTypeNames,ImGui::TNT_COUNT,ImGui::TestNodeFactory,NULL,-1); // last 2 args can be used to add only a subset of nodes (or to sort their order inside the context menu) nge.registerNodeTypeMaxAllowedInstances(ImGui::TNT_CUSTOM_ENUM_EDITOR_NODE,1); // Here we set the max number of allowed instances of the node (1) nge.show_style_editor = true; nge.show_load_save_buttons = true; // optional load the style (for all the editors: better call it in InitGL()): //NodeGraphEditor::Style::Load(NodeGraphEditor::GetStyle(),"nodeGraphEditor.style"); //-------------------------------------------------------------------------------- // Here we load "TestEnumNames" + the saved node graph editor from a singlefile. bool nodeGraphEditorSavePathLoaded = false; # if (defined(IMGUIHELPER_H_) && !defined(NO_IMGUIHELPER_SERIALIZATION) && !defined(NO_IMGUIHELPER_SERIALIZATION_LOAD)) { ImGuiHelper::Deserializer d(NodeGraphEditorSavePath); nodeGraphEditorSavePathLoaded = d.isValid(); const char* offset = 0; // Basically offset advances at each loading step TestEnumNamesLoad(d,&offset); // TestEnumNames nge.load(d,&offset); // nge } # endif if (!nodeGraphEditorSavePathLoaded) { // Starting items (sorted alphabetically) TestEnumNames.resize(3); strcpy(&TestEnumNames[0][0],"APPLE"); strcpy(&TestEnumNames[1][0],"LEMON"); strcpy(&TestEnumNames[2][0],"ORANGE"); // Optional: starting nodes and links (load from file instead):----------- nge.addNode(ImGui::TNT_CUSTOM_ENUM_EDITOR_NODE,ImVec2(40,50)); ImGui::Node* colorEnumUserNode1 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(40,180)); ImGui::Node* colorEnumUserNode2 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(300,180)); // optionally use e.g.: ImGui::ColorEnumUserNode::Cast(colorEnumUserNode1)->...; ImGui::Node* colorEnumUserNode3 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(550,180)); nge.addLink(colorEnumUserNode1, 0, colorEnumUserNode2, 0); nge.addLink(colorEnumUserNode1, 1, colorEnumUserNode2, 1); nge.addLink(colorEnumUserNode2, 0, colorEnumUserNode3, 0); nge.addLink(colorEnumUserNode2, 1, colorEnumUserNode3, 1); } } } void ResizeGL(int,int) {} void DestroyGL() { // We save "TestEnumNames" + the node graph editor together to a single file here # if (defined(IMGUIHELPER_H_) && !defined(NO_IMGUIHELPER_SERIALIZATION) && !defined(NO_IMGUIHELPER_SERIALIZATION_SAVE)) ImGuiHelper::Serializer s(NodeGraphEditorSavePath); TestEnumNamesSave(s); // TestEnumNames nge.save(s); // nge # endif } void DrawGL() -
Flix01 revised this gist
Apr 15, 2017 . 1 changed file with 0 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -253,7 +253,6 @@ bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if th # endif //NO_DYNAMIC_CAST if (--selectedEnumIndex<0) selectedEnumIndex=0; } } return nodeEdited; } -
Flix01 revised this gist
Apr 15, 2017 . 1 changed file with 181 additions and 48 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,15 +3,31 @@ // This file is intended to test/answer to https://github.com/Flix01/imgui/issues/15 // RESULT: // Dynamic enum works! // And if you can use dynamic_cast<>() making new Node types that use it is easier (non-intrusive) // Otherwise you must modify the code of CustomEnumEditorNode::render(...) for every new user class you add. // // Added also some code to serialize/deserialize the enum names ("TestEnumNames"). // Serialization/deserialization of the Node Graph Editor is not shown here, but it's trivial to add // and it's already shown in the default Node Graph Editor Demo. #include <imgui.h> // intellisense only #include <addons/imguinodegrapheditor/imguinodegrapheditor.h> // intellisense only #include <string.h> //strcpy //#define NO_DYNAMIC_CAST // More portable, but makes code more intrusive #ifndef NO_DYNAMIC_CAST class ITestEnum { public: virtual int& getSelectedItem()=0; virtual ~ITestEnum() {} }; #endif //NO_DYNAMIC_CAST #ifndef IM_PLACEMENT_NEW struct ImPlacementNewDummy {}; inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } @@ -24,6 +40,35 @@ inline void operator delete(void*, ImPlacementNewDummy, void*) {} #define MAX_ENUM_NAME_LENGTH 84 // in bytes typedef ImVector<char [MAX_ENUM_NAME_LENGTH]> TestEnumNamesType; // so that it works without STL (std::vector<std::string> will be easier to implement) TestEnumNamesType TestEnumNames; int TestEnumNamesInsert(const char* name) { if (!name) return -1; const int len = strlen(name); if (len<=0 || len+1>=MAX_ENUM_NAME_LENGTH) return -1; // We want to add the item in a sorted way. First we must calculate "itemPlacement" int itemPlacement = 0,comp = 0; for (int i=0,iSz=TestEnumNames.size();i<iSz;i++) { comp = strcmp(name,&TestEnumNames[i][0]); if (comp>0) ++itemPlacement; else if (comp==0) return -1; // already present } // Here we insert "name" at "itemPlacement" TestEnumNames.resize(TestEnumNames.size()+1); for (int i=TestEnumNames.size()-1;i>itemPlacement;--i) strcpy(&TestEnumNames[i][0],&TestEnumNames[i-1][0]); strcpy(&TestEnumNames[itemPlacement][0],name); return itemPlacement; } bool TestEnumNamesDelete(int itemIndex) { // We must delete the item int size = TestEnumNames.size(); for (int i=itemIndex;i<size-1;i++) { strcpy(&TestEnumNames[i][0],&TestEnumNames[i+1][0]); } --size;TestEnumNames.resize(size); return true; } // NODE DEFINITIONS ================================================================ @@ -74,42 +119,7 @@ class CustomEnumEditorNode : public Node { } protected: virtual bool render(float nodeWidth); public: @@ -128,8 +138,13 @@ class CustomEnumEditorNode : public Node { // casts: inline static ThisClass* Cast(Node* n) {return Node::Cast<ThisClass>(n,TYPE);} inline static const ThisClass* Cast(const Node* n) {return Node::Cast<ThisClass>(n,TYPE);} }; class ColorEnumUserNode : public Node #ifndef NO_DYNAMIC_CAST , public ITestEnum #endif //NO_DYNAMIC_CAST { protected: typedef Node Base; //Base Class typedef ColorEnumUserNode ThisClass; @@ -147,6 +162,9 @@ class ColorEnumUserNode : public Node { public: int& getSelectedItem() {return selectedEnumIndex;} // ITestEnum friend class CustomEnumEditorNode; // create: static ThisClass* Create(const ImVec2& pos) { // 1) allocation @@ -170,8 +188,76 @@ class ColorEnumUserNode : public Node { // casts: inline static ThisClass* Cast(Node* n) {return Node::Cast<ThisClass>(n,TYPE);} inline static const ThisClass* Cast(const Node* n) {return Node::Cast<ThisClass>(n,TYPE);} }; bool CustomEnumEditorNode::render(float nodeWidth) // should return "true" if the node has been edited and its values modified (to fire "edited callbacks") { bool nodeEdited = false; if (ImGui::InputText("New item",buf,MAX_ENUM_NAME_LENGTH,ImGuiInputTextFlags_EnterReturnsTrue)) { if (strlen(buf)>0) { const int itemIndex=TestEnumNamesInsert(buf); if (itemIndex>=0) { buf[0]='\0'; selectedEnumIndex = itemIndex; nodeEdited = true; //Now we must correct all the "selectedItem>=itemPlacement" in all the NodeGraphEditor ImGui::NodeGraphEditor& nge = getNodeGraphEditor(); # ifndef NO_DYNAMIC_CAST for (int i=0,iSz=nge.getNumNodes();i<iSz;i++) { ITestEnum* n = dynamic_cast<ITestEnum*>(nge.getNode(i)); if (n) { int& selectedIndexEnum = n->getSelectedItem(); if (selectedIndexEnum>=itemIndex) ++selectedIndexEnum; } } # else //NO_DYNAMIC_CAST ImVector<ImGui::Node*> nodes; // Similiar lines must be repeated for every new Node class definition that uses the dynamic enum nge.getAllNodesOfType(ImGui::TNT_CUSTOM_ENUM_USER_NODE,&nodes); for (int i=0,iSz=nodes.size();i<iSz;i++) { ImGui::ColorEnumUserNode& n = *ImGui::ColorEnumUserNode::Cast(nodes[i]); if (n.selectedEnumIndex>=itemIndex) ++n.selectedEnumIndex; } # endif //NO_DYNAMIC_CAST } } } fields[0].render(nodeWidth); if (TestEnumNames.size()>0) { ImGui::SameLine(); if (ImGui::SmallButton("x") && TestEnumNamesDelete(selectedEnumIndex)) { nodeEdited = true; //Now we must correct all the "selectedItem>=selectedEnumIndex" in all the NodeGraphEditor ImGui::NodeGraphEditor& nge = getNodeGraphEditor(); # ifndef NO_DYNAMIC_CAST for (int i=0,iSz=nge.getNumNodes();i<iSz;i++) { ITestEnum* n = dynamic_cast<ITestEnum*>(nge.getNode(i)); if (n) { int& selectedIndexEnum = n->getSelectedItem(); if (selectedIndexEnum>=selectedEnumIndex) --selectedIndexEnum; } } # else //NO_DYNAMIC_CAST ImVector<ImGui::Node*> nodes; // Similiar lines must be repeated for every new Node class definition that uses the dynamic enum nge.getAllNodesOfType(ImGui::TNT_CUSTOM_ENUM_USER_NODE,&nodes); for (int i=0,iSz=nodes.size();i<iSz;i++) { ImGui::ColorEnumUserNode& n = *ImGui::ColorEnumUserNode::Cast(nodes[i]); if (n.selectedEnumIndex>=selectedEnumIndex) --n.selectedEnumIndex; } # endif //NO_DYNAMIC_CAST if (--selectedEnumIndex<0) selectedEnumIndex=0; } ImGui::SetTooltip("delete item"); } return nodeEdited; } static Node* TestNodeFactory(int nt,const ImVec2& pos) { switch (nt) { case TNT_CUSTOM_ENUM_EDITOR_NODE: return CustomEnumEditorNode::Create(pos); @@ -187,22 +273,68 @@ static Node* TestNodeFactory(int nt,const ImVec2& pos) { // END NODE DEFINITIONS ============================================================ // Optional methods to load/save "TestEnumNames" #if (defined(IMGUIHELPER_H_) && !defined(NO_IMGUIHELPER_SERIALIZATION)) #ifndef NO_IMGUIHELPER_SERIALIZATION_SAVE bool TestEnumNamesSave(ImGuiHelper::Serializer& s) { const int size = TestEnumNames.size(); s.save(&size,"num_text_line_items"); s.saveTextLines(size,ImGui::CustomEnumEditorNode::GetTextFromEnumIndex,(void*)&TestEnumNames,"text_line_items"); return true; } inline bool TestEnumNamesSave(const char* filename) { ImGuiHelper::Serializer s(filename); return TestEnumNamesSave(s); } #endif //NO_IMGUIHELPER_SERIALIZATION_SAVE #ifndef NO_IMGUIHELPER_SERIALIZATION_LOAD static bool TestEnumNamesTypeLoadCallback(ImGuiHelper::FieldType ft,int numArrayElements,void* pValue,const char* name,void* userPtr) { TestEnumNamesType* vec = (TestEnumNamesType*) userPtr; if (strcmp(name,"num_text_line_items")==0) {vec->resize(*((int*)pValue));for (int i=0;i<vec->size();i++) (*vec)[i][0]='\0';} else if (ft==ImGui::FT_TEXTLINE && strcmp(name,"text_line_items")==0) { IM_ASSERT(numArrayElements<vec->size()); strcpy(&(*vec)[numArrayElements][0],(char*)pValue); if (numArrayElements==vec->size()-1) return true; } return false; } bool TestEnumNamesLoad(ImGuiHelper::Deserializer& d, const char ** pOptionalBufferStart=NULL) { const char* amount = pOptionalBufferStart ? (*pOptionalBufferStart) : 0; d.parse(TestEnumNamesTypeLoadCallback,(void*)&TestEnumNames,amount); if (pOptionalBufferStart) *pOptionalBufferStart=amount; return true; } bool TestEnumNamesLoad(const char* filename) { ImGuiHelper::Deserializer d(filename); return TestEnumNamesLoad(d); } #endif //NO_IMGUIHELPER_SERIALIZATION_LOAD #endif //NO_IMGUIHELPER_SERIALIZATION const char* TestEnumNamesSavePath = "testEnumNames.txt"; ImGui::NodeGraphEditor nge; // Mandatory methods void InitGL() { if (nge.isInited()) { // We should load "TestEnumNames" from a file here. Instead we do: # if (defined(IMGUIHELPER_H_) && !defined(NO_IMGUIHELPER_SERIALIZATION) && !defined(NO_IMGUIHELPER_SERIALIZATION_LOAD)) TestEnumNamesLoad(TestEnumNamesSavePath); # endif if (TestEnumNames.size()==0) { // Starting items (sorted alphabetically) TestEnumNames.resize(3); strcpy(&TestEnumNames[0][0],"APPLE"); strcpy(&TestEnumNames[1][0],"LEMON"); strcpy(&TestEnumNames[2][0],"ORANGE"); } // This adds entries to the "add node" context menu nge.registerNodeTypes(ImGui::TestNodeTypeNames,ImGui::TNT_COUNT,ImGui::TestNodeFactory,NULL,-1); // last 2 args can be used to add only a subset of nodes (or to sort their order inside the context menu) nge.registerNodeTypeMaxAllowedInstances(ImGui::TNT_CUSTOM_ENUM_EDITOR_NODE,1); // Here we set the max number of allowed instances of the node (1) // Optional: starting nodes and links (load from file instead):----------- nge.addNode(ImGui::TNT_CUSTOM_ENUM_EDITOR_NODE,ImVec2(40,50)); ImGui::Node* colorEnumUserNode1 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(40,180)); ImGui::Node* colorEnumUserNode2 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(300,180)); // optionally use e.g.: ImGui::ColorEnumUserNode::Cast(colorEnumUserNode1)->...; ImGui::Node* colorEnumUserNode3 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(550,180)); @@ -225,6 +357,9 @@ void InitGL() { void ResizeGL(int,int) {} void DestroyGL() { // We should save "TestEnumNames" to a file here # if (defined(IMGUIHELPER_H_) && !defined(NO_IMGUIHELPER_SERIALIZATION) && !defined(NO_IMGUIHELPER_SERIALIZATION_SAVE)) TestEnumNamesSave(TestEnumNamesSavePath); # endif } void DrawGL() { @@ -258,5 +393,3 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, return 0; } -
Flix01 created this gist
Apr 15, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,262 @@ // On Ubuntu, I can compile it with the following command line (provided that imgui.h is two folders up, and that I want to use glfw): // gcc -o basicExample mainBasic.cpp -I"../../" ../../imgui.cpp ../../imgui_draw.cpp -D"IMGUI_INCLUDE_IMGUI_USER_H" -D"IMGUI_INCLUDE_IMGUI_USER_INL" -I"/usr/include/GLFW" -D"IMGUI_USE_GLFW_BINDING" -L"/usr/lib/x86_64-linux-gnu" -lglfw -lX11 -lm -lGL -lstdc++ -s // This file is intended to test/answer to https://github.com/Flix01/imgui/issues/15 // RESULT: // As far as you append new enum items everything goes smooth, // but if you try to delete one, or to insert a new item in the middle of others // very difficult problems arise, since all users must update their selected item. #include <imgui.h> // intellisense only #include <addons/imguinodegrapheditor/imguinodegrapheditor.h> // intellisense only #include <string.h> //strcpy #ifndef IM_PLACEMENT_NEW struct ImPlacementNewDummy {}; inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } inline void operator delete(void*, ImPlacementNewDummy, void*) {} #define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR) #endif //IM_PLACEMENT_NEW // MY DATA STRUCTURE =============================================================== #define MAX_ENUM_NAME_LENGTH 84 // in bytes typedef ImVector<char [MAX_ENUM_NAME_LENGTH]> TestEnumNamesType; // so that it works without STL (std::vector<std::string> will be easier to implement) TestEnumNamesType TestEnumNames; // NODE DEFINITIONS ================================================================ namespace ImGui { enum TestNodeTypes { TNT_CUSTOM_ENUM_EDITOR_NODE = 0, TNT_CUSTOM_ENUM_USER_NODE, TNT_COUNT }; static const char* TestNodeTypeNames[TNT_COUNT] = {"Custom Enum Editor","Custom Enum User"}; class CustomEnumEditorNode : public Node { protected: typedef Node Base; //Base Class typedef CustomEnumEditorNode ThisClass; CustomEnumEditorNode() : Base() {} static const int TYPE = TNT_CUSTOM_ENUM_EDITOR_NODE; int selectedEnumIndex; // field char buf[MAX_ENUM_NAME_LENGTH]; public: virtual const char* getTooltip() const {return "CustomEnumEditorNode tooltip.";} virtual const char* getInfo() const {return "CustomEnumEditorNode info.\n\nThis is supposed to display some info about this node.";} /*virtual void getDefaultTitleBarColors(ImU32& defaultTitleTextColorOut,ImU32& defaultTitleBgColorOut,float& defaultTitleBgColorGradientOut) const { // [Optional Override] customize Node Title Colors [default values: 0,0,-1.f => do not override == use default values from the Style()] defaultTitleTextColorOut = IM_COL32(220,220,220,255);defaultTitleBgColorOut = IM_COL32(0,75,0,255);defaultTitleBgColorGradientOut = -1.f; }*/ // create: static ThisClass* Create(const ImVec2& pos) { // 1) allocation // MANDATORY (NodeGraphEditor::~NodeGraphEditor() will delete these with ImGui::MemFree(...)) // MANDATORY even with blank ctrs. Reason: ImVector does not call ctrs/dctrs on items. ThisClass* node = (ThisClass*) ImGui::MemAlloc(sizeof(ThisClass));IM_PLACEMENT_NEW (node) ThisClass(); // 2) main init node->init("CustomEnumEditorNode",pos,"","",TYPE); // 3) init fields ( this uses the node->fields variable; otherwise we should have overridden other virtual methods (to render and serialize) ) node->fields.addFieldEnum(&node->selectedEnumIndex,&CustomEnumEditorNode::GetNumEnumItems,&CustomEnumEditorNode::GetTextFromEnumIndex,"###CustomEnumEditor",NULL,&TestEnumNames); // 4) set (or load) field values node->selectedEnumIndex = 0; node->buf[0]='\0'; return node; } protected: virtual bool render(float nodeWidth) // should return "true" if the node has been edited and its values modified (to fire "edited callbacks") { bool nodeEdited = false; if (ImGui::InputText("New item",buf,MAX_ENUM_NAME_LENGTH,ImGuiInputTextFlags_EnterReturnsTrue)) { if (strlen(buf)>0) { // Well, here we should: 1) check if the enum already exists to skip it // and 2) we should insert the new item at the right place (sorted alphabetically) [*] // we don't: const int size = TestEnumNames.size(); TestEnumNames.resize(size+1); strcpy(&TestEnumNames[size][0],buf); buf[0]='\0'; selectedEnumIndex = size; nodeEdited = true; } } fields[0].render(nodeWidth); if (TestEnumNames.size()>0) { ImGui::SameLine(); if (ImGui::SmallButton("x")) { // We must delete the item int size = TestEnumNames.size(); for (int i=selectedEnumIndex;i<size-1;i++) { strcpy(&TestEnumNames[i][0],&TestEnumNames[i+1][0]); } --size;TestEnumNames.resize(size); if (--selectedEnumIndex<0) selectedEnumIndex=0; nodeEdited = true; // Easy... not at all: what happens to all the other nodes of type ColorEnumUserNode ? // Their selected item can be WRONG! And we don't fix this issue... // The same BIG problem happens when we implement [2] above: all the selectedEnumIndex will be wrong! ImGui::SetTooltip("delete item"); } } return nodeEdited; } public: static bool GetTextFromEnumIndex(void* data,int value,const char** pTxt) { if (!pTxt || !data) return false; const TestEnumNamesType& vec = *((const TestEnumNamesType*) data); *pTxt = (value>=0 && value<vec.size()) ? vec[value] : "UNKNOWN"; return true; } static int GetNumEnumItems(void* data) { if (!data) return 0; const TestEnumNamesType& vec = *((const TestEnumNamesType*) data); return vec.size(); } // casts: inline static ThisClass* Cast(Node* n) {return Node::Cast<ThisClass>(n,TYPE);} inline static const ThisClass* Cast(const Node* n) {return Node::Cast<ThisClass>(n,TYPE);} }; class ColorEnumUserNode : public Node { protected: typedef Node Base; //Base Class typedef ColorEnumUserNode ThisClass; ColorEnumUserNode() : Base() {} static const int TYPE = TNT_CUSTOM_ENUM_USER_NODE; int selectedEnumIndex; // field virtual const char* getTooltip() const {return "ColorEnumUserNode tooltip.";} virtual const char* getInfo() const {return "ColorEnumUserNode info.\n\nThis is supposed to display some info about this node.";} /*virtual void getDefaultTitleBarColors(ImU32& defaultTitleTextColorOut,ImU32& defaultTitleBgColorOut,float& defaultTitleBgColorGradientOut) const { // [Optional Override] customize Node Title Colors [default values: 0,0,-1.f => do not override == use default values from the Style()] defaultTitleTextColorOut = IM_COL32(220,220,220,255);defaultTitleBgColorOut = IM_COL32(0,75,0,255);defaultTitleBgColorGradientOut = -1.f; }*/ public: // create: static ThisClass* Create(const ImVec2& pos) { // 1) allocation // MANDATORY (NodeGraphEditor::~NodeGraphEditor() will delete these with ImGui::MemFree(...)) // MANDATORY even with blank ctrs. Reason: ImVector does not call ctrs/dctrs on items. ThisClass* node = (ThisClass*) ImGui::MemAlloc(sizeof(ThisClass));IM_PLACEMENT_NEW (node) ThisClass(); // 2) main init node->init("ColorEnumUserNode",pos,"in_a;in_b","out_a;out_b",TYPE); // 3) init fields ( this uses the node->fields variable; otherwise we should have overridden other virtual methods (to render and serialize) ) node->fields.addFieldEnum(&node->selectedEnumIndex,&CustomEnumEditorNode::GetNumEnumItems,&CustomEnumEditorNode::GetTextFromEnumIndex,"Selection","select your favourite",&TestEnumNames); // 4) set (or load) field values node->selectedEnumIndex = 0; return node; } // casts: inline static ThisClass* Cast(Node* n) {return Node::Cast<ThisClass>(n,TYPE);} inline static const ThisClass* Cast(const Node* n) {return Node::Cast<ThisClass>(n,TYPE);} }; static Node* TestNodeFactory(int nt,const ImVec2& pos) { switch (nt) { case TNT_CUSTOM_ENUM_EDITOR_NODE: return CustomEnumEditorNode::Create(pos); case TNT_CUSTOM_ENUM_USER_NODE: return ColorEnumUserNode::Create(pos); default: IM_ASSERT(true); // Missing node type creation return NULL; } return NULL; } } // namespace ImGui // END NODE DEFINITIONS ============================================================ ImGui::NodeGraphEditor nge; // Mandatory methods void InitGL() { if (nge.isInited()) { // We should load "TestEnumNames" from a file here. Instead we do: TestEnumNames.resize(3); strcpy(&TestEnumNames[0][0],"APPLE"); strcpy(&TestEnumNames[1][0],"LEMON"); strcpy(&TestEnumNames[2][0],"ORANGE"); // This adds entries to the "add node" context menu nge.registerNodeTypes(ImGui::TestNodeTypeNames,ImGui::TNT_COUNT,ImGui::TestNodeFactory,NULL,-1); // last 2 args can be used to add only a subset of nodes (or to sort their order inside the context menu) nge.registerNodeTypeMaxAllowedInstances(ImGui::TNT_CUSTOM_ENUM_EDITOR_NODE,1); // Here we set the max number of allowed instances of the node (1) // Optional: starting nodes and links (load from file instead):----------- ImGui::Node* customEnumEditorNode = nge.addNode(ImGui::TNT_CUSTOM_ENUM_EDITOR_NODE,ImVec2(40,50)); ImGui::Node* colorEnumUserNode1 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(40,180)); ImGui::Node* colorEnumUserNode2 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(300,180)); // optionally use e.g.: ImGui::ColorEnumUserNode::Cast(colorEnumUserNode1)->...; ImGui::Node* colorEnumUserNode3 = nge.addNode(ImGui::TNT_CUSTOM_ENUM_USER_NODE,ImVec2(550,180)); nge.addLink(colorEnumUserNode1, 0, colorEnumUserNode2, 0); nge.addLink(colorEnumUserNode1, 1, colorEnumUserNode2, 1); nge.addLink(colorEnumUserNode2, 0, colorEnumUserNode3, 0); nge.addLink(colorEnumUserNode2, 1, colorEnumUserNode3, 1); //------------------------------------------------------------------------------- //nge.load("nodeGraphEditor.nge"); // Please note than if the saved graph has nodes out of our active subset, they will be displayed as usual (it's not clear what should be done in this case: hope that's good enough, it's a user's mistake). //------------------------------------------------------------------------------- nge.show_style_editor = true; nge.show_load_save_buttons = true; // optional load the style (for all the editors: better call it in InitGL()): //NodeGraphEditor::Style::Load(NodeGraphEditor::GetStyle(),"nodeGraphEditor.style"); //-------------------------------------------------------------------------------- } } void ResizeGL(int,int) {} void DestroyGL() { // We should save "TestEnumNames" to a file here } void DrawGL() { ImImpl_ClearColorBuffer(ImVec4(0.6f, 0.6f, 0.6f, 1.0f)); // Warning: it does not clear the depth buffer static bool open = true; if (ImGui::Begin("Node Graph Editor", &open, ImVec2(1190,710),0.85f,ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings)) { nge.render(); } ImGui::End(); } #ifndef IMGUI_USE_AUTO_BINDING_WINDOWS // IMGUI_USE_AUTO_ definitions get defined automatically (e.g. do NOT touch them!) int main(int argc, char** argv) #else //IMGUI_USE_AUTO_BINDING_WINDOWS int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int iCmdShow) { #endif //IMGUI_USE_AUTO_BINDING_WINDOWS { ImImpl_InitParams* pInitParams = NULL; # ifndef IMGUI_USE_AUTO_BINDING_WINDOWS ImImpl_Main(pInitParams,argc,argv); # else //IMGUI_USE_AUTO_BINDING_WINDOWS ImImpl_WinMain(pInitParams,hInstance,hPrevInstance,lpCmdLine,iCmdShow); # endif //IMGUI_USE_AUTO_BINDING_WINDOWS return 0; }