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