1 /++ 2 + Authors: Stephan Dilly, lastname dot firstname at gmail dot com 3 + Copyright: MIT 4 +/ 5 module unecht.core.components.editor.editorGui; 6 7 version(UEIncludeEditor): 8 9 import unecht.core.component; 10 import unecht.core.components._editor; 11 import unecht.core.components.sceneNode; 12 import unecht.core.components.editor.ui.assetView; 13 import unecht.core.components.editor.ui.menuSystem; 14 import unecht.core.components.editor.ui.console; 15 import unecht.core.components.editor.ui.referenceEditor; 16 import unecht.core.components.editor.ui.dragDropEditor; 17 import unecht.core.components.editor.ui.fileDialog; 18 19 import unecht.core.components.internal.gui:UEGui; 20 21 import derelict.imgui.imgui; 22 23 /// 24 final class UEEditorGUI : UEComponent 25 { 26 mixin(UERegisterObject!()); 27 28 UEEditorMenuBar menuBar; 29 UEEditorAssetView assetView; 30 UEEditorConsole console; 31 UEReferenceEditor referenceEditor; 32 UEDragDropEditor dragDropEditor; 33 UEFileDialog fileDialog; 34 35 override void onCreate() { 36 super.onCreate; 37 38 assetView = entity.addComponent!UEEditorAssetView; 39 menuBar = entity.addComponent!UEEditorMenuBar; 40 console = entity.addComponent!UEEditorConsole; 41 referenceEditor = entity.addComponent!UEReferenceEditor; 42 dragDropEditor = entity.addComponent!UEDragDropEditor; 43 fileDialog = entity.addComponent!UEFileDialog; 44 } 45 46 //TODO: #127 47 void render() 48 { 49 import unecht.ue:ue; 50 51 { 52 const height = igGetItemsLineHeightWithSpacing(); 53 igSetNextWindowPos(ImVec2(0,ue.application.framebufferSize.height-height),ImGuiSetCond_Always); 54 55 igPushStyleColor(ImGuiCol_WindowBg, ImVec4(1,1,1,0)); 56 igBegin("editor",null, 57 ImGuiWindowFlags_AlwaysAutoResize| 58 ImGuiWindowFlags_NoTitleBar| 59 ImGuiWindowFlags_NoMove); 60 61 scope(exit) 62 { 63 igEnd(); 64 igPopStyleColor(); 65 } 66 67 import std.format:format; 68 UEGui.Text(format("EditorMode (%s with F1) [%0.1f fps]",EditorRootComponent.visible?"hide":"show",igGetIO().Framerate)); 69 } 70 71 if(EditorRootComponent.visible) 72 { 73 menuBar.render(); 74 if(showHirarchie) 75 { 76 renderScene(); 77 renderInspector(); 78 } 79 if(showDebug) 80 renderDebug(); 81 82 assetView.render(sceneWindowHeight); 83 84 if(console.enabled) 85 console.render(); 86 87 referenceEditor.render(); 88 89 dragDropEditor.render(); 90 91 fileDialog.render(); 92 } 93 } 94 95 private static void renderDebug() 96 { 97 igBegin("debug", &showDebug); 98 scope(exit) igEnd(); 99 100 import unecht.core.profiler:UEProfiling; 101 102 igPlotLines("framestimes",UEProfiling.frameTimes.ptr,cast(int)UEProfiling.frameTimes.length,0,null,float.max,float.max,ImVec2(0,100)); 103 igPlotLines("fps",UEProfiling.framerates.ptr,cast(int)UEProfiling.framerates.length,0,null,float.max,float.max,ImVec2(0,100)); 104 } 105 106 /// 107 package static bool showHirarchie = true; 108 package static bool showDebug = false; 109 110 private static float sceneWindowWidth; 111 private static float sceneWindowHeight; 112 /// 113 private static void renderScene() 114 { 115 import unecht.ue:ue; 116 117 const top = igGetItemsLineHeightWithSpacing(); 118 igSetNextWindowPos(ImVec2(0,top), ImGuiSetCond_Always); 119 120 igBegin("scene",null,ImGuiWindowFlags_NoMove); 121 scope(exit){igEnd();} 122 123 foreach(n; ue.scene.root.children) 124 { 125 renderSceneNode(n); 126 } 127 128 sceneWindowWidth = igGetWindowWidth(); 129 sceneWindowHeight = igGetWindowHeight(); 130 } 131 132 /// 133 private static void renderSceneNode(UESceneNode _node) 134 { 135 if(_node.hideInHirarchie && !showHiddenNodes) 136 return; 137 138 const canExpand = _node.children.length>0; 139 140 if(canExpand) 141 { 142 if(_node.hideInHirarchie) 143 igPushStyleColor(ImGuiCol_Text, ImVec4(0.9f,0.9f,0.9f,0.5f)); 144 145 const expanded = UEGui.TreeNode(cast(void*)(_node.entity), _node.entity.name); 146 147 if(_node.hideInHirarchie) 148 igPopStyleColor(); 149 150 if(igIsItemActive()) 151 { 152 if(EditorRootComponent.currentEntity !is _node.entity) 153 EditorRootComponent.selectEntity(_node.entity); 154 } 155 if(igIsItemHovered() && igIsMouseDoubleClicked(0)) 156 EditorRootComponent.lookAtNode(_node); 157 158 if(!expanded) 159 return; 160 161 foreach(n; _node.children) 162 { 163 renderSceneNode(n); 164 } 165 166 igTreePop(); 167 } 168 else 169 { 170 if(_node.hideInHirarchie) 171 igPushStyleColor(ImGuiCol_Text, ImVec4(0.9f,0.9f,0.9f,0.5f)); 172 173 igBullet(); 174 igPushIdPtr(cast(void*)(_node.entity)); 175 auto isSelected = EditorRootComponent.currentEntity is _node.entity; 176 if(UEGui.Selectable(_node.entity.name,isSelected)) 177 { 178 if(isSelected) 179 EditorRootComponent.selectEntity(null); 180 else 181 EditorRootComponent.selectEntity(_node.entity); 182 } 183 igPopId(); 184 185 if(_node.hideInHirarchie) 186 igPopStyleColor(); 187 188 if(igIsItemHovered() && igIsMouseDoubleClicked(0)) 189 EditorRootComponent.lookAtNode(_node); 190 } 191 } 192 193 /// 194 private static void renderInspector() 195 { 196 if(!EditorRootComponent.currentEntity) 197 return; 198 199 const top = igGetItemsLineHeightWithSpacing(); 200 igSetNextWindowPos(ImVec2(sceneWindowWidth+1,top),ImGuiSetCond_Always); 201 bool closed; 202 igBegin("inspector",&closed, 203 ImGuiWindowFlags_AlwaysAutoResize| 204 ImGuiWindowFlags_NoCollapse| 205 ImGuiWindowFlags_NoMove| 206 ImGuiWindowFlags_NoResize); 207 208 scope(exit)igEnd(); 209 210 if(closed) 211 { 212 EditorRootComponent.selectEntity(null); 213 return; 214 } 215 216 string name = EditorRootComponent.currentEntity.name; 217 UEGui.InputText("name",name); 218 EditorRootComponent.currentEntity.name = name; 219 220 foreach(int i, c; EditorRootComponent.currentEntity.components) 221 { 222 const isSceneNode = i == 0; 223 renderInspectorComponent(c,isSceneNode); 224 } 225 226 renderInspectorFooter(); 227 228 if(component2Remove) 229 { 230 //TODO: broken ? 231 component2Remove.entity.removeComponent(component2Remove); 232 component2Remove = null; 233 } 234 if(componentToAdd.length>0) 235 { 236 EditorRootComponent.currentEntity.addComponent(componentToAdd); 237 componentToAdd.length = 0; 238 } 239 } 240 241 /// 242 private static void renderInspectorComponent(UEComponent c, bool isSceneNode) 243 { 244 auto openNode = false; 245 246 if(!isSceneNode) 247 { 248 igPushIdPtr(cast(void*)c); 249 openNode = UEGui.TreeNode(c.typename); 250 } 251 else 252 { 253 UEGui.Text("UESceneNode"); 254 } 255 256 if(openNode || isSceneNode) 257 { 258 if(!openNode) 259 igIndent(); 260 261 renderInspectorSameline(c,!isSceneNode); 262 263 import unecht.core.componentManager; 264 if(auto renderer = c.typename in UEComponentsManager.editors) 265 { 266 renderer.render(c); 267 } 268 269 if(openNode) 270 igTreePop(); 271 else 272 igUnindent(); 273 } 274 else 275 { 276 renderInspectorSameline(c, !isSceneNode); 277 } 278 279 if(isSceneNode) 280 igSeparator(); 281 else 282 igPopId(); 283 } 284 285 static bool showHiddenNodes=false; 286 @MenuItem("view/show hidden nodes") 287 static private void toggleShowHiddenNodes() 288 { 289 showHiddenNodes = !showHiddenNodes; 290 } 291 292 static UEComponent componentEdit; 293 private static void renderInspectorSameline(UEComponent c, bool allowEdit) 294 { 295 auto subtext = " "; 296 if(c.enabled) 297 subtext = "X"; 298 299 ImVec2 size; 300 igGetWindowContentRegionMax(&size); 301 const wndWidth = cast(int)size.x; 302 303 igCalcTextSize(&size,"#"); 304 const charWidth = 2 + cast(int)size.x*2; 305 306 if(allowEdit) 307 { 308 igSameLine(wndWidth-charWidth*2); 309 if(UEGui.SmallButton("#")) 310 { 311 componentEdit = c; 312 igOpenPopup("compedit"); 313 } 314 315 renderComponentEdit(); 316 } 317 318 igSameLine(wndWidth - charWidth); 319 if(UEGui.SmallButton(subtext)) 320 c.enabled = !c.enabled; 321 } 322 323 static UEComponent component2Remove; 324 325 private static void renderComponentEdit() 326 { 327 bool menuOpen = igBeginPopup("compedit"); 328 scope(exit){ if(menuOpen) igEndPopup(); } 329 330 if(!menuOpen && !componentEdit) 331 { 332 igCloseCurrentPopup(); 333 componentEdit = null; 334 return; 335 } 336 if(menuOpen) 337 { 338 UEGui.Text("edit"); 339 igSeparator(); 340 341 if(UEGui.Button("remove")) 342 { 343 component2Remove = componentEdit; 344 componentEdit = null; 345 igCloseCurrentPopup(); 346 } 347 } 348 } 349 350 static string componentToAdd; 351 352 private static void renderInspectorFooter() 353 { 354 igSeparator(); 355 if(UEGui.Button("add component...")) 356 igOpenPopup("addcomp"); 357 358 static string filterString; 359 360 bool menuOpen=igBeginPopup("addcomp"); 361 scope(exit){if(menuOpen) igEndPopup();} 362 if(!menuOpen) 363 { 364 filterString.length=0; 365 return; 366 } 367 else 368 { 369 UEGui.InputText("filter",filterString); 370 igSeparator(); 371 372 import unecht.core.componentManager; 373 foreach(c; UEComponentsManager.componentNames) 374 { 375 import std..string; 376 if(c.indexOf(filterString,CaseSensitive.no)==-1) 377 continue; 378 379 if(UEGui.Selectable(c,false)) 380 { 381 componentToAdd = c; 382 filterString.length=0; 383 } 384 } 385 } 386 } 387 }