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 }