1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.panels.nodes;
8 import creator.viewport.vertex;
9 import creator.actions;
10 import creator.panels;
11 import creator;
12 import creator.widgets;
13 import creator.core;
14 import creator.utils;
15 import inochi2d;
16 import std.string;
17 import std.format;
18 import std.conv;
19 import i18n;
20 
21 /**
22     The logger frame
23 */
24 class NodesPanel : Panel {
25 protected:
26     void treeSetEnabled(Node n, bool enabled) {
27         n.enabled = enabled;
28         foreach(child; n.children) {
29             treeSetEnabled(child, enabled);
30         }
31     }
32 
33     void nodeActionsPopup(bool isRoot = false)(Node n) {
34         if (igIsItemClicked(ImGuiMouseButton.Right)) {
35             igOpenPopup("NodeActionsPopup");
36         }
37 
38         if (igBeginPopup("NodeActionsPopup")) {
39             
40             auto selected = incSelectedNodes();
41             
42             if (igBeginMenu(__("Add"), true)) {
43 
44                 igPushFont(incIconFont());
45                     igText(incTypeIdToIcon("Node").ptr);
46                 igPopFont();
47                 igSameLine(0, 2);
48                 if (igMenuItem(__("Node"), "", false, true)) incAddChildWithHistory(new Node(n), n);
49                 
50                 igPushFont(incIconFont());
51                     igText(incTypeIdToIcon("Mask").ptr);
52                 igPopFont();
53                 igSameLine(0, 2);
54                 if (igMenuItem(__("Mask"), "", false, true)) {
55                     MeshData empty;
56                     incAddChildWithHistory(new Mask(empty, n), n);
57                 }
58                 
59                 igPushFont(incIconFont());
60                     igText(incTypeIdToIcon("PathDeform").ptr);
61                 igPopFont();
62                 igSameLine(0, 2);
63                 if (igMenuItem(__("PathDeform"), "", false, true)) incAddChildWithHistory(new PathDeform(n), n);
64                 
65                 igEndMenu();
66             }
67 
68             static if (!isRoot) {
69                 if (igMenuItem(n.enabled ? /* Option to hide the node (and subnodes) */ __("Hide") :  /* Option to show the node (and subnodes) */ __("Show"))) {
70                     n.enabled = !n.enabled;
71                 }
72                 
73                 if (igMenuItem(__("Delete"), "", false, !isRoot)) {
74 
75                     foreach(sn; selected) {
76                         incDeleteChildWithHistory(sn);
77                     }
78 
79                     // Make sure we don't keep selecting a node we've removed
80                     incSelectNode(null);
81                 }
82                 
83 
84                 if (igBeginMenu(__("More Info"), true)) {
85                     if (selected.length > 1) {
86                         foreach(sn; selected) {
87                             
88                             // %s is the name of the node in the More Info menu
89                             // %lu is the UUID of the node in the More Info menu
90                             igText(__("%s ID: %lu"), sn.name.ptr, sn.uuid);
91                         }
92                     } else {
93                         // %lu is the UUID of the node in the More Info menu
94                         igText(__("ID: %lu"), n.uuid);
95                     }
96 
97                     igEndMenu();
98                 }
99             }
100             igEndPopup();
101         }
102     }
103 
104     void treeAddNode(bool isRoot = false)(ref Node n) {
105         igTableNextRow();
106 
107         auto io = igGetIO();
108 
109         // // Draw Enabler for this node first
110         // igTableSetColumnIndex(1);
111         // igPushFont(incIconFont());
112         //     igText(n.enabled ? "\ue8f4" : "\ue8f5");
113         // igPopFont();
114 
115 
116         // Prepare node flags
117         ImGuiTreeNodeFlags flags;
118         if (n.children.length == 0) flags |= ImGuiTreeNodeFlags.Leaf;
119         flags |= ImGuiTreeNodeFlags.DefaultOpen;
120         flags |= ImGuiTreeNodeFlags.OpenOnArrow;
121 
122         // Then draw the node tree index
123         igTableSetColumnIndex(0);
124         bool open = igTreeNodeEx(cast(void*)n.uuid, flags, "");
125 
126             // Show node entry stuff
127             igSameLine(0, 4);
128 
129             auto selectedNodes = incSelectedNodes();
130             igPushID(n.uuid);
131                     bool selected = incNodeInSelection(n);
132 
133                     igPushFont(incIconFont());
134                         static if (!isRoot) {
135                             if (n.enabled) igText(incTypeIdToIcon(n.typeId).ptr);
136                             else igTextDisabled(incTypeIdToIcon(n.typeId).ptr);
137                         } else {
138                             igText("");
139                         }
140                     igPopFont();
141                     igSameLine(0, 2);
142 
143                     if (igSelectable(isRoot ? __("Puppet") : n.name.toStringz, selected, ImGuiSelectableFlags.None, ImVec2(0, 0))) {
144                         switch(incEditMode) {
145                             default:
146                                 if (selected) {
147                                     if (incSelectedNodes().length > 1) {
148                                         if (io.KeyCtrl) incRemoveSelectNode(n);
149                                         else incSelectNode(n);
150                                     } else {
151                                         incFocusCamera(n);
152                                     }
153                                 } else {
154                                     if (io.KeyCtrl) incAddSelectNode(n);
155                                     else incSelectNode(n);
156                                 }
157                                 break;
158                         }
159                     }
160                     this.nodeActionsPopup!isRoot(n);
161 
162                     static if (!isRoot) {
163                         if(igBeginDragDropSource(ImGuiDragDropFlags.SourceAllowNullID)) {
164                             igSetDragDropPayload("_PUPPETNTREE", cast(void*)&n, (&n).sizeof, ImGuiCond.Always);
165                             if (selectedNodes.length > 1) {
166                                 foreach(node; selectedNodes) {
167                                     igText(node.name.toStringz);
168                                 }
169                             } else {
170                                 igText(n.name.toStringz);
171                             }
172                             igEndDragDropSource();
173                         }
174                     }
175             igPopID();
176 
177             // Only allow reparenting one node
178             if (selectedNodes.length < 2) {
179                 if(igBeginDragDropTarget()) {
180                     ImGuiPayload* payload = igAcceptDragDropPayload("_PUPPETNTREE");
181                     if (payload !is null) {
182                         Node payloadNode = *cast(Node*)payload.Data;
183                         
184                         if (payloadNode.canReparent(n)) {
185                             incMoveChildWithHistory(payloadNode, n);
186                         }
187                         
188                         igTreePop();
189                         return;
190                     }
191                     igEndDragDropTarget();
192                 }
193             }
194 
195         if (open) {
196             // Draw children
197             foreach(child; n.children) {
198                 treeAddNode(child);
199             }
200             igTreePop();
201         }
202         
203 
204     }
205 
206     void treeAddDrawable(ref Drawable n) {
207         igTextColored(ImVec4(0.6f, 0f, 0f, 0f), __("Node list is unavailable while editing meshes"));
208         // igTableNextRow();
209         // igTableSetColumnIndex(0);
210         
211         // ImGuiTreeNodeFlags flags;
212         // flags |= ImGuiTreeNodeFlags.Leaf;
213         // flags |= ImGuiTreeNodeFlags.DefaultOpen;
214         // flags |= ImGuiTreeNodeFlags.OpenOnArrow;
215 
216         // igTreeNodeEx(cast(void*)n.uuid, flags, "");
217         //     // Show node entry stuff
218         //     igSameLine(0, 4);
219 
220         //     igPushID(n.uuid);
221 
222         //         bool selected = incNodeInSelection(n);
223 
224         //         igPushFont(incIconFont());
225         //             igText(incTypeIdToIcon(n.typeId).ptr);
226         //         igPopFont();
227         //         igSameLine(0, 2);
228 
229         //         if (igSelectable(n.name.toStringz, selected, ImGuiSelectableFlags.None, ImVec2(0, 0))) {
230         //             if (selected) {
231         //                 incFocusCamera(n, vec2(0, 0));
232         //             }
233         //             incSelectNode(n);
234         //             incVertexEditSetTarget(n);
235         //         }
236         //         // this.nodeActionsPopup(n);
237         //     igPopID();
238         // igTreePop();
239     }
240 
241     override
242     void onUpdate() {
243 
244         if (incEditMode == EditMode.ModelEdit){ 
245             auto io = igGetIO();
246             if (io.KeyCtrl && igIsKeyPressed(igGetKeyIndex(ImGuiKey.A), false)) {
247                 incSelectAll();
248             }
249         }
250 
251         igBeginChild_Str("NodesMain", ImVec2(0, -30), false);
252             igPushStyleVar(ImGuiStyleVar.CellPadding, ImVec2(4, 1));
253             igPushStyleVar(ImGuiStyleVar.IndentSpacing, 14);
254 
255             if (igBeginTable("NodesContent", 2, ImGuiTableFlags.ScrollX, ImVec2(0, 0), 0)) {
256                 igTableSetupColumn("Nodes", ImGuiTableColumnFlags.WidthFixed, 0, 0);
257                 //igTableSetupColumn("Visibility", ImGuiTableColumnFlags_WidthFixed, 32, 1);
258                 
259                 if (incEditMode == EditMode.ModelEdit) {
260                     treeAddNode!true(incActivePuppet.root);
261                 } else {
262                     foreach(drawable; incDrawables()) {
263                             treeAddDrawable(drawable);
264                     }
265                 }
266 
267                 igEndTable();
268             }
269             if (igIsItemClicked(ImGuiMouseButton.Left)) {
270                 incSelectNode(null);
271             }
272             igPopStyleVar();
273             igPopStyleVar();
274         igEndChild();
275 
276         igSeparator();
277         igSpacing();
278         
279         igPushFont(incIconFont());
280             if (incEditMode() == EditMode.ModelEdit) {
281                 auto selected = incSelectedNodes();
282                 if (igButton("", ImVec2(24, 24))) {
283                     foreach(payloadNode; selected) incDeleteChildWithHistory(payloadNode);
284                 }
285 
286                 if(igBeginDragDropTarget()) {
287                     ImGuiPayload* payload = igAcceptDragDropPayload("_PUPPETNTREE");
288                     if (payload !is null) {
289                         Node payloadNode = *cast(Node*)payload.Data;
290 
291                         if (selected.length > 1) {
292                             foreach(pn; selected) incDeleteChildWithHistory(pn);
293                             incSelectNode(null);
294                         } else {
295 
296                             // Make sure we don't keep selecting a node we've removed
297                             if (incNodeInSelection(payloadNode)) {
298                                 incSelectNode(null);
299                             }
300 
301                             incDeleteChildWithHistory(payloadNode);
302                         }
303                         
304                         igPopFont();
305                         return;
306                     }
307                     igEndDragDropTarget();
308                 }
309             }
310         igPopFont();
311 
312     }
313 
314 public:
315 
316     this() {
317         super("Nodes", _("Nodes"), true);
318         flags |= ImGuiWindowFlags.NoScrollbar;
319     }
320 }
321 
322 /**
323     Generate nodes frame
324 */
325 mixin incPanel!NodesPanel;
326 
327