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