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("Composite").ptr);
61                 igPopFont();
62                 igSameLine(0, 2);
63                 if (igMenuItem(__("Composite"), "", false, true)) {
64                     incAddChildWithHistory(new Composite(n), n);
65                 }
66                 
67                 igPushFont(incIconFont());
68                     igText(incTypeIdToIcon("SimplePhysics").ptr);
69                 igPopFont();
70                 igSameLine(0, 2);
71                 if (igMenuItem(__("Simple Physics"), "", false, true)) incAddChildWithHistory(new SimplePhysics(n), n);
72 
73                 igEndMenu();
74             }
75 
76             static if (!isRoot) {
77                 if (igMenuItem(n.enabled ? /* Option to hide the node (and subnodes) */ __("Hide") :  /* Option to show the node (and subnodes) */ __("Show"))) {
78                     n.enabled = !n.enabled;
79                 }
80                 
81                 if (igMenuItem(__("Delete"), "", false, !isRoot)) {
82 
83                     if (selected.length > 1) {
84                         foreach(sn; selected) {
85                             incDeleteChildWithHistory(sn);
86                         }
87                     } else {
88                         incDeleteChildWithHistory(n);
89                     }
90 
91                     // Make sure we don't keep selecting a node we've removed
92                     incSelectNode(null);
93                 }
94                 
95 
96                 if (igBeginMenu(__("More Info"), true)) {
97                     if (selected.length > 1) {
98                         foreach(sn; selected) {
99                             
100                             // %s is the name of the node in the More Info menu
101                             // %lu is the UUID of the node in the More Info menu
102                             igText(__("%s ID: %lu"), sn.name.ptr, sn.uuid);
103                         }
104                     } else {
105                         // %lu is the UUID of the node in the More Info menu
106                         igText(__("ID: %lu"), n.uuid);
107                     }
108 
109                     igEndMenu();
110                 }
111             }
112             igEndPopup();
113         }
114     }
115 
116     void treeAddNode(bool isRoot = false)(ref Node n) {
117         igTableNextRow();
118 
119         auto io = igGetIO();
120 
121         // // Draw Enabler for this node first
122         // igTableSetColumnIndex(1);
123         // igPushFont(incIconFont());
124         //     igText(n.enabled ? "\ue8f4" : "\ue8f5");
125         // igPopFont();
126 
127 
128         // Prepare node flags
129         ImGuiTreeNodeFlags flags;
130         if (n.children.length == 0) flags |= ImGuiTreeNodeFlags.Leaf;
131         flags |= ImGuiTreeNodeFlags.DefaultOpen;
132         flags |= ImGuiTreeNodeFlags.OpenOnArrow;
133 
134         // Then draw the node tree index
135         igTableSetColumnIndex(0);
136         bool open = igTreeNodeEx(cast(void*)n.uuid, flags, "");
137 
138             // Show node entry stuff
139             igSameLine(0, 4);
140 
141             auto selectedNodes = incSelectedNodes();
142             igPushID(n.uuid);
143                     bool selected = incNodeInSelection(n);
144 
145                     igPushFont(incIconFont());
146                         static if (!isRoot) {
147                             if (n.enabled) igText(incTypeIdToIcon(n.typeId).ptr);
148                             else igTextDisabled(incTypeIdToIcon(n.typeId).ptr);
149                         } else {
150                             igText("");
151                         }
152                     igPopFont();
153                     igSameLine(0, 2);
154 
155                     if (igSelectable(isRoot ? __("Puppet") : n.name.toStringz, selected, ImGuiSelectableFlags.None, ImVec2(0, 0))) {
156                         switch(incEditMode) {
157                             default:
158                                 if (selected) {
159                                     if (incSelectedNodes().length > 1) {
160                                         if (io.KeyCtrl) incRemoveSelectNode(n);
161                                         else incSelectNode(n);
162                                     } else {
163                                         incFocusCamera(n);
164                                     }
165                                 } else {
166                                     if (io.KeyCtrl) incAddSelectNode(n);
167                                     else incSelectNode(n);
168                                 }
169                                 break;
170                         }
171                     }
172                     this.nodeActionsPopup!isRoot(n);
173 
174                     static if (!isRoot) {
175                         if(igBeginDragDropSource(ImGuiDragDropFlags.SourceAllowNullID)) {
176                             igSetDragDropPayload("_PUPPETNTREE", cast(void*)&n, (&n).sizeof, ImGuiCond.Always);
177                             if (selectedNodes.length > 1) {
178                                 foreach(node; selectedNodes) {
179                                     igText(node.name.toStringz);
180                                 }
181                             } else {
182                                 igText(n.name.toStringz);
183                             }
184                             igEndDragDropSource();
185                         }
186                     }
187             igPopID();
188 
189             // Only allow reparenting one node
190             if (selectedNodes.length < 2) {
191                 if(igBeginDragDropTarget()) {
192                     ImGuiPayload* payload = igAcceptDragDropPayload("_PUPPETNTREE");
193                     if (payload !is null) {
194                         Node payloadNode = *cast(Node*)payload.Data;
195                         
196                         if (payloadNode.canReparent(n)) {
197                             incMoveChildWithHistory(payloadNode, n);
198                         }
199                         
200                         igTreePop();
201                         return;
202                     }
203                     igEndDragDropTarget();
204                 }
205             }
206 
207         if (open) {
208             // Draw children
209             foreach(i, child; n.children) {
210                 igPushID(cast(int)i);
211                     igTableNextRow();
212                     igTableSetColumnIndex(0);
213                     igInvisibleButton("###TARGET", ImVec2(128, 4));
214 
215                     if(igBeginDragDropTarget()) {
216                         ImGuiPayload* payload = igAcceptDragDropPayload("_PUPPETNTREE");
217                         if (payload !is null) {
218                             Node payloadNode = *cast(Node*)payload.Data;
219                             
220                             if (payloadNode.canReparent(n)) {
221                                 auto idx = payloadNode.getIndexInNode(n);
222                                 if (idx >= 0) {
223                                     payloadNode.insertInto(n, clamp(idx < i ? i-1 : i, 0, n.children.length));
224                                 } else {
225                                     payloadNode.insertInto(n, clamp(cast(ptrdiff_t)i, 0, n.children.length));
226                                 }
227                             }
228                             
229                             igPopID();
230                             igTreePop();
231                             return;
232                         }
233                         igEndDragDropTarget();
234                     }
235                 igPopID();
236 
237                 treeAddNode(child);
238             }
239             igTreePop();
240         }
241         
242 
243     }
244 
245     override
246     void onUpdate() {
247 
248         if (incEditMode == EditMode.ModelEdit) { 
249             if (!incArmedParameter) {
250                 auto io = igGetIO();
251                 if (io.KeyCtrl && igIsKeyPressed(igGetKeyIndex(ImGuiKey.A), false)) {
252                     incSelectAll();
253                 }
254             }
255         }
256 
257         if (incEditMode == EditMode.VertexEdit) {
258             igText(__("In vertex edit mode..."));
259             return;
260         }
261 
262         igBeginChild_Str("NodesMain", ImVec2(0, -30), false);
263             igPushStyleVar(ImGuiStyleVar.CellPadding, ImVec2(4, 1));
264             igPushStyleVar(ImGuiStyleVar.IndentSpacing, 14);
265 
266             if (igBeginTable("NodesContent", 2, ImGuiTableFlags.ScrollX, ImVec2(0, 0), 0)) {
267                 igTableSetupColumn("Nodes", ImGuiTableColumnFlags.WidthFixed, 0, 0);
268                 //igTableSetupColumn("Visibility", ImGuiTableColumnFlags_WidthFixed, 32, 1);
269                 
270                 if (incEditMode == EditMode.ModelEdit) {
271                     treeAddNode!true(incActivePuppet.root);
272                 }
273 
274                 igEndTable();
275             }
276             if (igIsItemClicked(ImGuiMouseButton.Left)) {
277                 incSelectNode(null);
278             }
279             igPopStyleVar();
280             igPopStyleVar();
281         igEndChild();
282 
283         igSeparator();
284         igSpacing();
285         
286         igPushFont(incIconFont());
287             if (incEditMode() == EditMode.ModelEdit) {
288                 auto selected = incSelectedNodes();
289                 if (igButton("", ImVec2(24, 24))) {
290                     foreach(payloadNode; selected) incDeleteChildWithHistory(payloadNode);
291                 }
292 
293                 if(igBeginDragDropTarget()) {
294                     ImGuiPayload* payload = igAcceptDragDropPayload("_PUPPETNTREE");
295                     if (payload !is null) {
296                         Node payloadNode = *cast(Node*)payload.Data;
297 
298                         if (selected.length > 1) {
299                             foreach(pn; selected) incDeleteChildWithHistory(pn);
300                             incSelectNode(null);
301                         } else {
302 
303                             // Make sure we don't keep selecting a node we've removed
304                             if (incNodeInSelection(payloadNode)) {
305                                 incSelectNode(null);
306                             }
307 
308                             incDeleteChildWithHistory(payloadNode);
309                         }
310                         
311                         igPopFont();
312                         return;
313                     }
314                     igEndDragDropTarget();
315                 }
316             }
317         igPopFont();
318 
319     }
320 
321 public:
322 
323     this() {
324         super("Nodes", _("Nodes"), true);
325         flags |= ImGuiWindowFlags.NoScrollbar;
326     }
327 }
328 
329 /**
330     Generate nodes frame
331 */
332 mixin incPanel!NodesPanel;
333 
334