1 /** 2 Pre-parsed scene tree path. 3 4 Copyright: 5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017 Godot-D contributors 8 Copyright (c) 2022 Godot-DLang contributors 9 10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 11 12 13 */ 14 module godot.nodepath; 15 16 import core.lifetime : emplace; 17 18 import godot.string; 19 import godot.stringname; 20 import godot.abi; 21 import godot.builtins; 22 23 // HACK: string 24 // There's basically a lot of those hacks 25 26 /** 27 A pre-parsed relative or absolute path in a scene tree, for use with $(D Node.getNode) and similar functions. It can reference a node, a resource within a node, or a property of a node or resource. For instance, `"Path2D/PathFollow2D/Sprite:texture:size"` would refer to the size property of the texture resource on the node named “Sprite” which is a child of the other named nodes in the path. Note that if you want to get a resource, you must end the path with a colon, otherwise the last element will be used as a property name. 28 29 Exporting a `NodePath` variable will give you a node selection widget in the properties panel of the editor, which can often be useful. 30 31 A `NodePath` is made up of a list of node names, a list of “subnode” (resource) names, and the name of a property in the final node or resource. 32 */ 33 struct NodePath { 34 //@nogc nothrow: 35 36 package(godot) union nodepath { 37 godot_node_path _node_path; 38 NodePath_Bind _bind; 39 } 40 41 package(godot) nodepath _nodepath; 42 alias _nodepath this; 43 44 this(this) { 45 // do a copy as godot might free it right after property set 46 if (_node_path._opaque.ptr) { 47 godot_node_path other = _node_path; 48 //emplace(&this, _bind.new1(other)); 49 //_godot_api.variant_new_copy(&_node_path, &other); 50 } 51 } 52 53 package(godot) this(godot_node_path n) { 54 _node_path = n; 55 } 56 57 this(in String from) { 58 emplace(&this, _bind.new2(from)); 59 // //_godot_api.node_path_new(&_node_path, &from._godot_string); 60 } 61 62 this(in string name) { 63 String from = String(name); 64 // // _godot_api.node_path_new(&_node_path, &from._godot_string); 65 emplace(&this, _bind.new2(from)); 66 } 67 68 // quick hack for beta2+ changes 69 this(in NodePath other) { 70 _node_path = other._node_path; 71 } 72 73 bool opEquals(in NodePath other) const { 74 if (_node_path == other._node_path) 75 return true; 76 //return _godot_api.node_path_operator_equal(&_node_path, &other._node_path); 77 return _bind == other._bind; 78 } 79 80 StringName getName(in int idx) const { 81 return _bind.getName(idx); 82 //godot_string str = _godot_api.node_path_get_name(&_node_path, idx); 83 //return String(str); 84 } 85 86 int getNameCount() const { 87 //return _godot_api.node_path_get_name_count(&_node_path); 88 return cast(int) _bind.getNameCount(); 89 } 90 91 StringName getSubname(in int idx) const { 92 return _bind.getSubname(idx); 93 //godot_string str = _godot_api.node_path_get_subname(&_node_path, idx); 94 //return String(str); 95 } 96 97 int getSubnameCount() const { 98 return cast(int) _bind.getSubnameCount(); 99 //return _godot_api.node_path_get_subname_count(&_node_path); 100 } 101 102 bool isAbsolute() const { 103 return _bind.isAbsolute(); 104 //return cast(bool)_godot_api.node_path_is_absolute(&_node_path); 105 } 106 107 bool isEmpty() const { 108 return _bind.isEmpty(); 109 //return cast(bool)_godot_api.node_path_is_empty(&_node_path); 110 } 111 112 String str() const { 113 return String_Bind.new3(cast() this); 114 //godot_string str = _godot_api.node_path_as_string(&_node_path); 115 //return String(str); 116 } 117 118 /// Splits a NodePath into a main node path and a property subpath (starting 119 /// with a ':'). The second element is left empty if there is no property. 120 NodePath[2] split() const { 121 static immutable wchar_t colon = ':'; 122 NodePath[2] ret; 123 if (_node_path == godot_node_path.init) 124 return ret; 125 String path = str(); 126 immutable(wchar_t)[] data = path.data(); 127 if (data.length == 0) 128 return ret; 129 if (data[0] == colon) { 130 ret[1] = cast() this; 131 return ret; 132 } 133 134 ptrdiff_t colonIndex = 0; 135 // Windows requires UTF-16 decoding to find the ':' 136 static if (is(wchar_t == wchar)) { 137 import utf_bc; 138 139 enum TextFormat gdFormat = TextFormat.UTF_16; 140 auto decoded = data.decode!gdFormat; 141 do { 142 dchar front = decoded.front; 143 decoded.popFront(); 144 if (front == colon) 145 break; 146 colonIndex += codeLength!gdFormat(front); 147 } 148 while (!decoded.empty); 149 if (decoded.empty) 150 colonIndex = -1; 151 } else { 152 import std.algorithm : countUntil; 153 154 colonIndex = data[].countUntil(colon); 155 } 156 157 // node only 158 if (colonIndex == -1) { 159 ret[0] = cast() this; 160 return ret; 161 } 162 ret[0] = NodePath(String(data[0 .. colonIndex])); 163 ret[1] = NodePath(String(data[colonIndex .. $])); 164 return ret; 165 } 166 167 //this(ref return scope const NodePath other) // copy ctor 168 NodePath opAssign(ref const NodePath other) { 169 _node_path = other._node_path; 170 //godot_node_path_copy(&_node_path, &other._node_path); 171 if (other._node_path._opaque.ptr) { 172 import godot.api; 173 return _bind.new1(other); 174 } 175 176 //print(this); 177 178 return this; 179 } 180 181 NodePath opAssign(in NodePath other) { 182 _node_path = other._node_path; 183 //godot_node_path_copy(&_node_path, &other._node_path); 184 if (other._node_path._opaque.ptr) { 185 import godot.api; 186 187 print(other); 188 return _bind.new1(other); 189 } 190 //import godot.api; 191 //print(other); 192 //print(this); 193 194 return this; 195 } 196 197 ~this() { 198 //_godot_api.variant_destroy(&_node_path); 199 //if (_node_path._opaque) 200 // _bind._destructor(); 201 //_node_path._opaque = 0; 202 } 203 }