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 }