1 module unecht.core.defaultInspector;
2 
3 /// UDA
4 struct UEInspectorTooltip
5 {
6 	string txt;
7 }
8 
9 /// UDA
10 struct UEInspectorRange(T)
11 {
12 	T min;
13 	T max;
14 }
15 
16 version(UEIncludeEditor){
17 
18 	import unecht.meta.uda;
19 	import unecht.core.object;
20 	import unecht.core.entity;
21 	import unecht.core.component;
22 	import unecht.core.components.internal.gui;
23 	import unecht.core.components.editor.ui.dragDropEditor;
24 	import unecht.core.componentManager:IComponentEditor;
25 
26 	import derelict.imgui.imgui;
27 
28 	import gl3n.linalg;
29 
30 	///
31 	private static bool renderBaseClasses(T)(T _v)
32 	{
33 		import std.traits:BaseClassesTuple;
34 
35 		static if(BaseClassesTuple!T.length > 1)
36 		{
37 			return renderMembers!(BaseClassesTuple!T[0])(_v);
38 		}
39 	}
40 
41 	///
42 	enum renderMembersMethodMixinString = q{
43 		import std.traits:FieldNameTuple;
44 
45 		enum wholeSerialize = hasUDA!(T,Serialize) || forceSerialize;
46 
47 		bool changesInMembers;
48 		foreach(idx, name; FieldNameTuple!T)
49 		{
50 			static if(wholeSerialize || hasUDA!(_v.tupleof[idx],Serialize))
51 			{
52 				const(char)* tooltip;
53 
54 				static if(hasUDA!(_v.tupleof[idx],UEInspectorTooltip))
55 				{
56 					tooltip = getUDA!(_v.tupleof[idx],UEInspectorTooltip).txt;
57 				}
58 
59 				enum hasIntRange = hasUDA!(_v.tupleof[idx],UEInspectorRange!int);
60 				enum hasFloatRange = hasUDA!(_v.tupleof[idx],UEInspectorRange!float);
61 
62 				static if(hasIntRange || hasFloatRange)
63 				{
64 					static if(hasIntRange)
65 					{
66 						enum min = getUDA!(_v.tupleof[idx],UEInspectorRange!int).min;
67 						enum max = getUDA!(_v.tupleof[idx],UEInspectorRange!int).max;
68 					}
69 					else
70 					{
71 						enum min = getUDA!(_v.tupleof[idx],UEInspectorRange!float).min;
72 						enum max = getUDA!(_v.tupleof[idx],UEInspectorRange!float).max;
73 					}
74 
75 					if(renderEditor!(typeof(_v.tupleof[idx]))(name, tooltip, _v.tupleof[idx], min, max))
76 						changesInMembers = true;
77 				}
78 				else
79 				{
80 					if(renderEditor!(typeof(_v.tupleof[idx]))(name, tooltip, _v.tupleof[idx]))
81 						changesInMembers = true;
82 				}
83 			}
84 		}
85 
86 		return changesInMembers;
87 	};
88 
89 	///
90 	private static bool renderMembers(T)(T _v)
91 		if(is(T==class))
92 	{
93 		enum forceSerialize = false;
94 		mixin(renderMembersMethodMixinString);
95 	}
96 
97 	///
98 	private static bool renderMembers(T)(ref T _v)
99 		if(is(T==struct))
100 	{
101 		enum forceSerialize = true;
102 		mixin(renderMembersMethodMixinString);
103 	}
104 
105 	/// pointers
106 	private static bool renderEditor(T)(string _fieldname, const(char)* _tooltip, ref T _v)
107 		if(is(T : P*,P))
108 	{
109 		UEGui.Text("no editor for pointers: " ~ T.stringof ~ " ('" ~ _fieldname ~ "')");
110 		return false;
111 	}
112 
113 	/// struct
114 	private static bool renderEditor(T)(string _fieldname, const(char)* _tooltip, ref T _v)
115 		if(is(T == struct))
116 	{
117 		igPushIdPtr(cast(void*)&_v);
118 		auto open = UEGui.TreeNode(_fieldname);
119 
120 		bool res;
121 
122 		if(open)
123 			res = renderMembers(_v);
124 
125 		if(open)
126 			igTreePop();
127 
128 		igPopId();
129 
130 		return res;
131 	}
132 
133 	/// class
134 	private static bool renderEditor(T)(string _fieldname, const(char)* _tooltip, ref T _v)
135 		if(is(T == class))
136 	{
137 		import unecht.core.assets.texture;
138 
139 		static if(is(T:UEComponent))
140 		{
141 			if(_v.entity)
142 				UEGui.Text(_fieldname ~ ": \"" ~ _v.entity.name ~ "\"");
143 			else
144 				UEGui.Text(_fieldname ~ ": \"<null>\"");
145 		}
146 		else static if(is(T:UEEntity))
147 		{
148 			UEGui.Text(_fieldname ~ ": \"" ~ _v.name ~ "\"");
149 		}
150 		else static if(is(T:UETexture))
151 		{
152 			import unecht.core.components.editor.ui.referenceEditor;
153 			import unecht.core.assetDatabase;
154 
155 			auto assetName = UEAssetDatabase.getAssetName(_v);
156 			if(assetName.length == 0)
157 				assetName = "<null>";
158 
159 			if(UEGui.Button(_fieldname ~ ": " ~ assetName))
160 				UEReferenceEditor.open(cast(UEObject*)&_v);
161 
162 			enum TEX_SIZE = 120;
163 			if(_v !is null && _v.isValid)
164 				igImage(_v.driverHandle, ImVec2(TEX_SIZE,TEX_SIZE));
165 
166 			UEDragDropEditor.canDrop(typeid(UETexture));
167 
168 			if(auto dragSource = UEDragDropEditor.isDropped())
169 				_v = cast(T)dragSource;
170 		}
171 
172 		//TODO: implement
173 		return false;
174 	}
175 
176 	//TODO: array editor
177 	/// array
178 	private static bool renderEditor(T:E[],E)(string _fieldname, const(char)* _tooltip, ref T _v)
179 	{
180 		UEGui.Text("no editor for array: " ~ T.stringof ~ " ('" ~ _fieldname ~ "')");
181 		return false;
182 	}
183 
184 	/// vec3
185 	private static bool renderEditor(T:vec3)(string _fieldname, const(char)* _tooltip, ref T _v)
186 	{
187 		return UEGui.InputVec(_fieldname, _v);
188 	}
189 
190 	/// vec3
191 	private static bool renderEditor(T:vec2)(string _fieldname, const(char)* _tooltip, ref T _v)
192 	{
193 		return UEGui.InputVec(_fieldname, _v);
194 	}
195 
196 	/// assoc array
197 	private static bool renderEditor(T:E[K],E,K)(string _fieldname, const(char)* _tooltip, ref T _v)
198 	{
199 		UEGui.Text("no editor for assoc.array: " ~ T.stringof ~ " ('" ~ _fieldname ~ "')");
200 		return false;
201 	}
202 
203 	/// bool
204 	private static bool renderEditor(T:bool)(string _fieldname, const(char)* _tooltip, ref T _v)
205 	{
206 		auto changes = UEGui.checkbox(_fieldname, _v);
207 
208 		if (_tooltip !is null && igIsItemHovered())
209 			igSetTooltip(_tooltip);
210 
211 		return changes;
212 	}
213 
214 	/// float
215 	private static bool renderEditor(T)(string _fieldname, const(char)* _tooltip, ref T _v, float _min=float.min_normal, float _max=float.max)
216 		if(is(T == float))
217 	{
218 		auto changes = UEGui.DragFloat(_fieldname, _v, _min, _max);
219 
220 		if (_tooltip !is null && igIsItemHovered())
221 			igSetTooltip(_tooltip);
222 
223 		return changes;
224 	}
225 
226 	/// int
227 	private static bool renderEditor(T)(string _fieldname, const(char)* _tooltip, ref T _v, int _min=int.min, int _max=int.max)
228 		if(is(T:int) && !is(T == enum))
229 	{
230 		int i = _v;
231 		auto changes = UEGui.DragInt(_fieldname, i, _min, _max);
232 		_v = cast(T)i;
233 
234 		if (_tooltip !is null && igIsItemHovered())
235 			igSetTooltip(_tooltip);
236 
237 		return changes;
238 	}
239 
240 	/// enum
241 	private static bool renderEditor(T)(string _fieldname, const(char)* _tooltip, ref T _v)
242 		if(is(T == enum))
243 	{
244 		auto changes = UEGui.EnumCombo(_fieldname, _v);
245 
246 		if (_tooltip !is null && igIsItemHovered())
247 			igSetTooltip(_tooltip);
248 
249 		return changes;
250 	}
251 
252 	///
253 	private static bool renderObjectEditor(T)(T _v)
254 	{
255 		auto changesInBase = renderBaseClasses!T(_v);
256 		auto changesInMembers = renderMembers!T(_v);
257 
258 		return changesInBase || changesInMembers;
259 	}
260 
261 	///
262 	static class UEDefaultInspector(T) : IComponentEditor
263 	{
264 		///
265 		override bool render(UEObject _component)
266 		{
267 			//pragma(msg, "\nEDITOR: "~T.stringof);
268 
269 			auto thisT = cast(T)_component;
270 
271 			return renderObjectEditor(thisT);
272 		}
273 	}
274 }
275 else
276 {
277 	struct UEDefaultInspector(T){}
278 }