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