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