1 /* 2 Copyright © 2020, Inochi2D Project 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 5 Authors: Luna Nielsen 6 */ 7 module creator.core; 8 import creator.core.dpi; 9 import creator.core.input; 10 import creator.core.egg; 11 import creator.panels; 12 import creator.windows; 13 import creator.utils.link; 14 import creator; 15 import creator.widgets.dialog; 16 import creator.backend.gl; 17 18 import std.exception; 19 20 import bindbc.sdl; 21 import bindbc.opengl; 22 import inochi2d; 23 import tinyfiledialogs; 24 import std.string; 25 import std.stdio; 26 import std.conv; 27 import std.range : repeat; 28 29 public import bindbc.imgui; 30 public import bindbc.imgui.ogl; 31 public import creator.core.settings; 32 public import creator.core.actionstack; 33 public import creator.core.tasks; 34 public import creator.core.path; 35 public import creator.core.font; 36 public import creator.core.dpi; 37 import i18n; 38 39 version(linux) import dportals; 40 41 version(Windows) { 42 import core.sys.windows.windows; 43 import core.sys.windows.winuser; 44 45 // Windows 8.1+ DPI awareness context enum 46 enum DPIAwarenessContext { 47 DPI_AWARENESS_CONTEXT_UNAWARE = 0, 48 DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 1, 49 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 2, 50 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 3 51 } 52 53 // Windows 8.1+ DPI awareness enum 54 enum ProcessDPIAwareness { 55 PROCESS_DPI_UNAWARE = 0, 56 PROCESS_SYSTEM_DPI_AWARE = 1, 57 PROCESS_PER_MONITOR_DPI_AWARE = 2 58 } 59 60 61 void incSetWin32DPIAwareness() { 62 void* userDLL, shcoreDLL; 63 64 bool function() dpiAwareFunc8; 65 HRESULT function(DPIAwarenessContext) dpiAwareFuncCtx81; 66 HRESULT function(ProcessDPIAwareness) dpiAwareFunc81; 67 68 userDLL = SDL_LoadObject("USER32.DLL"); 69 if (userDLL) { 70 dpiAwareFunc8 = cast(typeof(dpiAwareFunc8)) SDL_LoadFunction(userDLL, "SetProcessDPIAware"); 71 dpiAwareFuncCtx81 = cast(typeof(dpiAwareFuncCtx81)) SDL_LoadFunction(userDLL, "SetProcessDpiAwarenessContext"); 72 } 73 74 shcoreDLL = SDL_LoadObject("SHCORE.DLL"); 75 if (shcoreDLL) { 76 dpiAwareFunc81 = cast(typeof(dpiAwareFunc81)) SDL_LoadFunction(shcoreDLL, "SetProcessDpiAwareness"); 77 } 78 79 if (dpiAwareFuncCtx81) { 80 dpiAwareFuncCtx81(DPIAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); 81 dpiAwareFuncCtx81(DPIAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); 82 } else if (dpiAwareFunc81) { 83 dpiAwareFunc81(ProcessDPIAwareness.PROCESS_PER_MONITOR_DPI_AWARE); 84 } else if (dpiAwareFunc8) dpiAwareFunc8(); 85 86 87 // Unload the DLLs 88 if (userDLL) SDL_UnloadObject(userDLL); 89 if (shcoreDLL) SDL_UnloadObject(shcoreDLL); 90 } 91 } 92 93 private { 94 SDL_GLContext gl_context; 95 SDL_Window* window; 96 ImGuiIO* io; 97 bool done = false; 98 ImGuiID viewportDock; 99 100 version (InBranding) { 101 Texture incLogoI2D; 102 Texture incLogo; 103 Texture incAda; 104 } 105 Texture incGrid; 106 107 ImFont* mainFont; 108 109 bool isDarkMode = true; 110 string[] files; 111 bool isWayland; 112 bool isTilingWM; 113 114 115 ImVec4[ImGuiCol.COUNT] incDarkModeColors; 116 ImVec4[ImGuiCol.COUNT] incLightModeColors; 117 118 } 119 120 bool incShowStatsForNerds; 121 bool incShouldPostProcess = false; 122 123 bool incIsWayland() { 124 return isWayland; 125 } 126 127 bool incIsTilingWM() { 128 return isTilingWM; 129 } 130 131 /** 132 Finalizes everything by freeing imgui resources, etc. 133 */ 134 void incFinalize() { 135 136 // This is important to prevent thread leakage 137 import creator.viewport.test : incViewportTestWithdraw; 138 incViewportTestWithdraw(); 139 140 // Save settings 141 igSaveIniSettingsToDisk(igGetIO().IniFilename); 142 143 // Cleanup 144 incGLBackendShutdown(); 145 ImGui_ImplSDL2_Shutdown(); 146 igDestroyContext(null); 147 148 SDL_GL_DeleteContext(gl_context); 149 SDL_DestroyWindow(window); 150 SDL_Quit(); 151 } 152 153 /** 154 Gets dockspace of the viewport 155 */ 156 ImGuiID incGetViewportDockSpace() { 157 return viewportDock; 158 } 159 160 /** 161 Opens Window 162 */ 163 void incOpenWindow() { 164 import std.process : environment; 165 166 switch(environment.get("XDG_SESSION_DESKTOP")) { 167 case "i3": 168 169 // Items beyond this point are just guesstimations. 170 case "awesome": 171 case "bspwm": 172 case "dwm": 173 case "echinus": 174 case "euclid-wm": 175 case "herbstluftwm": 176 case "leftwm": 177 case "notion": 178 case "qtile": 179 case "ratpoison": 180 case "snapwm": 181 case "stumpwm": 182 case "subtle": 183 case "wingo": 184 case "wmfs": 185 case "xmonad": 186 case "wayfire": 187 case "river": 188 case "labwc": 189 isTilingWM = true; 190 break; 191 192 default: 193 isTilingWM = false; 194 break; 195 } 196 197 auto sdlSupport = loadSDL(); 198 enforce(sdlSupport != SDLSupport.noLibrary, "SDL2 library not found!"); 199 enforce(sdlSupport != SDLSupport.badLibrary, "Bad SDL2 library found!"); 200 201 version(BindImGui_Dynamic) { 202 auto imSupport = loadImGui(); 203 enforce(imSupport != ImGuiSupport.noLibrary, "cimgui library not found!"); 204 205 // HACK: For some reason this check fails on some macOS and Linux installations 206 version(Windows) enforce(imSupport != ImGuiSupport.badLibrary, "Bad cimgui library found!"); 207 } 208 209 SDL_Init(SDL_INIT_EVERYTHING); 210 211 version(Windows) { 212 incSetWin32DPIAwareness(); 213 } 214 215 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE); 216 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 217 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); 218 219 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 220 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 221 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 222 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); 223 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); 224 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 225 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); 226 227 SDL_WindowFlags flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; 228 229 if (incSettingsGet!bool("WinMax", false)) { 230 flags |= SDL_WINDOW_MAXIMIZED; 231 } 232 233 // Don't make KDE freak out when Inochi Creator opens 234 if (!incSettingsGet!bool("DisableCompositor")) SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); 235 236 version(InBranding) { 237 debug string WIN_TITLE = "Inochi Creator "~_("(Debug Mode)"); 238 else string WIN_TITLE = "Inochi Creator "~INC_VERSION; 239 } else string WIN_TITLE = "Inochi Creator "~_("(Unsupported)"); 240 241 window = SDL_CreateWindow( 242 WIN_TITLE.toStringz, 243 SDL_WINDOWPOS_UNDEFINED, 244 SDL_WINDOWPOS_UNDEFINED, 245 cast(uint)incSettingsGet!int("WinW", 1280), 246 cast(uint)incSettingsGet!int("WinH", 800), 247 flags 248 ); 249 SDL_SetWindowMinimumSize(window, 960, 720); 250 251 // On Linux we want to check whether the window was created under wayland or x11 252 version(linux) { 253 SDL_SysWMinfo info; 254 SDL_GetWindowWMInfo(window, &info); 255 isWayland = info.subsystem == SDL_SYSWM_TYPE.SDL_SYSWM_WAYLAND; 256 } 257 258 GLSupport support; 259 gl_context = SDL_GL_CreateContext(window); 260 SDL_GL_SetSwapInterval(1); 261 262 // Load GL 3 263 support = loadOpenGL(); 264 switch(support) { 265 case GLSupport.noLibrary: 266 throw new Exception("OpenGL library could not be loaded!"); 267 268 case GLSupport.noContext: 269 throw new Exception("No valid OpenGL context was found!"); 270 271 default: break; 272 } 273 274 275 import std.string : fromStringz; 276 version(Windows) { 277 278 // Windows is heck when it comes to /SUBSYSTEM:windows 279 } else { 280 debug { 281 writefln("GLInfo:\n\t%s\n\t%s\n\t%s\n\t%s\n\tgls=%s", 282 glGetString(GL_VERSION).fromStringz, 283 glGetString(GL_VENDOR).fromStringz, 284 glGetString(GL_RENDERER).fromStringz, 285 glGetString(GL_SHADING_LANGUAGE_VERSION).fromStringz, 286 support 287 ); 288 } 289 } 290 291 // Setup Inochi2D 292 inInit(() { return igGetTime(); }); 293 294 version(InBranding) incInitAda(); 295 incCreateContext(); 296 297 ShallowTexture tex; 298 version (InBranding) { 299 300 // Load image resources 301 tex = ShallowTexture(cast(ubyte[])import("logo.png")); 302 inTexPremultiply(tex.data); 303 incLogoI2D = new Texture(tex); 304 305 // Load image resources 306 tex = ShallowTexture(cast(ubyte[])import("icon.png")); 307 inTexPremultiply(tex.data); 308 incLogo = new Texture(tex); 309 310 // Set X11 window icon 311 version(linux) { 312 if (!isWayland) { 313 SDL_SetWindowIcon(window, SDL_CreateRGBSurfaceWithFormatFrom(tex.data.ptr, tex.width, tex.height, 32, 4*tex.width, SDL_PIXELFORMAT_RGBA32)); 314 } 315 } 316 317 tex = ShallowTexture(cast(ubyte[])import("ui/ui-ada.png")); 318 inTexPremultiply(tex.data); 319 incAda = new Texture(tex); 320 } 321 322 // Grid texture 323 tex = ShallowTexture(cast(ubyte[])import("ui/grid.png")); 324 inTexPremultiply(tex.data); 325 incGrid = new Texture(tex); 326 incGrid.setFiltering(Filtering.Point); 327 incGrid.setWrapping(Wrapping.Repeat); 328 329 // Load Settings 330 incShowStatsForNerds = incSettingsCanGet("NerdStats") ? incSettingsGet!bool("NerdStats") : false; 331 332 version(linux) { 333 dpInit(); 334 } 335 } 336 337 void incCreateContext() { 338 339 // Setup IMGUI 340 auto ctx = igCreateContext(null); 341 io = igGetIO(); 342 343 // Setup font handling 344 incInitFonts(); 345 346 import std.file : exists; 347 if (!exists(incGetAppImguiConfigFile())) { 348 // TODO: Setup a base config 349 } 350 351 352 // Copy string out of GC memory to make sure it doesn't get yeeted before imgui exits. 353 import core.stdc.stdlib : malloc; 354 import core.stdc.string : memcpy; 355 io.IniFilename = cast(char*)malloc(incGetAppImguiConfigFile().length+1); 356 memcpy(cast(void*)io.IniFilename, toStringz(incGetAppImguiConfigFile), incGetAppImguiConfigFile().length+1); 357 igLoadIniSettingsFromDisk(io.IniFilename); 358 359 incSetDarkMode(incSettingsGet!bool("DarkMode", true)); 360 361 io.ConfigFlags |= ImGuiConfigFlags.DockingEnable; // Enable Docking 362 io.ConfigWindowsResizeFromEdges = true; // Enable Edge resizing 363 version (OSX) io.ConfigMacOSXBehaviors = true; // macOS Behaviours on macOS 364 365 // Force C locale due to imgui removing support for setting decimal separator. 366 import i18n.culture : i18nSetLocale; 367 i18nSetLocale("C"); 368 369 // NOTE: Viewports break DPI scaling system, as such if Viewports is enabled 370 // we will be disable DPI scaling. 371 version(NoUIScaling) { 372 if (!incIsTilingWM) io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable; // Enable Viewports (causes freezes) 373 } else { 374 incInitDPIScaling(); 375 } 376 377 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Navigation 378 ImGui_ImplSDL2_InitForOpenGL(window, gl_context); 379 incGLBackendInit(null); 380 381 incInitStyling(); 382 incInitDialogs(); 383 } 384 385 386 /** 387 Initialize styling 388 */ 389 void incInitStyling() { 390 //style.WindowBorderSize = 0; 391 auto style = igGetStyle(); 392 style.FrameBorderSize = 1; 393 style.TabBorderSize = 1; 394 style.ChildBorderSize = 1; 395 style.PopupBorderSize = 1; 396 style.FrameBorderSize = 1; 397 style.TabBorderSize = 1; 398 399 style.WindowRounding = 4; 400 style.ChildRounding = 0; 401 style.FrameRounding = 3; 402 style.PopupRounding = 6; 403 style.ScrollbarRounding = 18; 404 style.GrabRounding = 3; 405 style.LogSliderDeadzone = 6; 406 style.TabRounding = 6; 407 408 style.IndentSpacing = 10; 409 style.ItemSpacing.y = 3; 410 style.FramePadding.y = 4; 411 412 style.GrabMinSize = 13; 413 style.ScrollbarSize = 14; 414 style.ChildBorderSize = 1; 415 416 // Don't draw the silly roll menu 417 style.WindowMenuButtonPosition = ImGuiDir.None; 418 419 // macOS support 420 version(OSX) style.WindowTitleAlign = ImVec2(0.5, 0.5); 421 422 423 igStyleColorsDark(style); 424 style.Colors[ImGuiCol.Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 425 style.Colors[ImGuiCol.TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); 426 style.Colors[ImGuiCol.WindowBg] = ImVec4(0.17f, 0.17f, 0.17f, 1.00f); 427 style.Colors[ImGuiCol.ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); 428 style.Colors[ImGuiCol.PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); 429 style.Colors[ImGuiCol.Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.16f); 430 style.Colors[ImGuiCol.BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.16f); 431 style.Colors[ImGuiCol.FrameBg] = ImVec4(0.12f, 0.12f, 0.12f, 1.00f); 432 style.Colors[ImGuiCol.FrameBgHovered] = ImVec4(0.15f, 0.15f, 0.15f, 0.40f); 433 style.Colors[ImGuiCol.FrameBgActive] = ImVec4(0.22f, 0.22f, 0.22f, 0.67f); 434 style.Colors[ImGuiCol.TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f); 435 style.Colors[ImGuiCol.TitleBgActive] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 436 style.Colors[ImGuiCol.TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); 437 style.Colors[ImGuiCol.MenuBarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f); 438 style.Colors[ImGuiCol.ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); 439 style.Colors[ImGuiCol.ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); 440 style.Colors[ImGuiCol.ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); 441 style.Colors[ImGuiCol.ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); 442 style.Colors[ImGuiCol.CheckMark] = ImVec4(0.76f, 0.76f, 0.76f, 1.00f); 443 style.Colors[ImGuiCol.SliderGrab] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f); 444 style.Colors[ImGuiCol.SliderGrabActive] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); 445 style.Colors[ImGuiCol.Button] = ImVec4(0.39f, 0.39f, 0.39f, 0.40f); 446 style.Colors[ImGuiCol.ButtonHovered] = ImVec4(0.44f, 0.44f, 0.44f, 1.00f); 447 style.Colors[ImGuiCol.ButtonActive] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); 448 style.Colors[ImGuiCol.Header] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f); 449 style.Colors[ImGuiCol.HeaderHovered] = ImVec4(0.28f, 0.28f, 0.28f, 0.80f); 450 style.Colors[ImGuiCol.HeaderActive] = ImVec4(0.44f, 0.44f, 0.44f, 1.00f); 451 style.Colors[ImGuiCol.Separator] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 452 style.Colors[ImGuiCol.SeparatorHovered] = ImVec4(0.29f, 0.29f, 0.29f, 0.78f); 453 style.Colors[ImGuiCol.SeparatorActive] = ImVec4(0.47f, 0.47f, 0.47f, 1.00f); 454 style.Colors[ImGuiCol.ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.00f); 455 style.Colors[ImGuiCol.ResizeGripHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.00f); 456 style.Colors[ImGuiCol.ResizeGripActive] = ImVec4(0.55f, 0.55f, 0.56f, 0.00f); 457 style.Colors[ImGuiCol.Tab] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 458 style.Colors[ImGuiCol.TabHovered] = ImVec4(0.34f, 0.34f, 0.34f, 0.80f); 459 style.Colors[ImGuiCol.TabActive] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f); 460 style.Colors[ImGuiCol.TabUnfocused] = ImVec4(0.14f, 0.14f, 0.14f, 0.97f); 461 style.Colors[ImGuiCol.TabUnfocusedActive] = ImVec4(0.17f, 0.17f, 0.17f, 1.00f); 462 style.Colors[ImGuiCol.DockingPreview] = ImVec4(0.62f, 0.68f, 0.75f, 0.70f); 463 style.Colors[ImGuiCol.DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); 464 style.Colors[ImGuiCol.PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); 465 style.Colors[ImGuiCol.PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); 466 style.Colors[ImGuiCol.PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); 467 style.Colors[ImGuiCol.PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); 468 style.Colors[ImGuiCol.TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f); 469 style.Colors[ImGuiCol.TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); 470 style.Colors[ImGuiCol.TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); 471 style.Colors[ImGuiCol.TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); 472 style.Colors[ImGuiCol.TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); 473 style.Colors[ImGuiCol.TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); 474 style.Colors[ImGuiCol.DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); 475 style.Colors[ImGuiCol.NavHighlight] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f); 476 style.Colors[ImGuiCol.NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); 477 style.Colors[ImGuiCol.NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); 478 style.Colors[ImGuiCol.ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); 479 incDarkModeColors = style.Colors.dup; 480 481 igStyleColorsLight(style); 482 style.Colors[ImGuiCol.Border] = ImVec4(0.8, 0.8, 0.8, 0.5); 483 style.Colors[ImGuiCol.BorderShadow] = ImVec4(0, 0, 0, 0.05); 484 style.Colors[ImGuiCol.TitleBg] = ImVec4(0.902, 0.902, 0.902, 1); 485 style.Colors[ImGuiCol.TitleBgActive] = ImVec4(0.98, 0.98, 0.98, 1); 486 style.Colors[ImGuiCol.Separator] = ImVec4(0.86, 0.86, 0.86, 1); 487 style.Colors[ImGuiCol.ScrollbarGrab] = ImVec4(0.68, 0.68, 0.68, 1); 488 style.Colors[ImGuiCol.ScrollbarGrabActive] = ImVec4(0.68, 0.68, 0.68, 1); 489 style.Colors[ImGuiCol.ScrollbarGrabHovered] = ImVec4(0.64, 0.64, 0.64, 1); 490 style.Colors[ImGuiCol.FrameBg] = ImVec4(1, 1, 1, 1); 491 style.Colors[ImGuiCol.FrameBgHovered] = ImVec4(0.78, 0.88, 1, 1); 492 style.Colors[ImGuiCol.FrameBgActive] = ImVec4(0.76, 0.86, 1, 1); 493 style.Colors[ImGuiCol.Button] = ImVec4(0.98, 0.98, 0.98, 1); 494 style.Colors[ImGuiCol.ButtonHovered] = ImVec4(1, 1, 1, 1); 495 style.Colors[ImGuiCol.ButtonActive] = ImVec4(0.8, 0.8, 0.8, 1); 496 style.Colors[ImGuiCol.CheckMark] = ImVec4(0, 0, 0, 1); 497 style.Colors[ImGuiCol.Tab] = ImVec4(0.98, 0.98, 0.98, 1); 498 style.Colors[ImGuiCol.TabHovered] = ImVec4(1, 1, 1, 1); 499 style.Colors[ImGuiCol.TabActive] = ImVec4(0.8, 0.8, 0.8, 1); 500 style.Colors[ImGuiCol.TabUnfocused] = ImVec4(0.92, 0.92, 0.92, 1); 501 style.Colors[ImGuiCol.TabUnfocusedActive] = ImVec4(0.88, 0.88, 0.88, 1); 502 style.Colors[ImGuiCol.MenuBarBg] = ImVec4(0.863, 0.863, 0.863, 1); 503 style.Colors[ImGuiCol.PopupBg] = ImVec4(0.941, 0.941, 0.941, 1); 504 style.Colors[ImGuiCol.Header] = ImVec4(0.990, 0.990, 0.990, 1); 505 style.Colors[ImGuiCol.HeaderHovered] = ImVec4(1, 1, 1, 1); 506 incLightModeColors = style.Colors.dup; 507 508 style.Colors = isDarkMode ? incDarkModeColors : incLightModeColors; 509 } 510 511 void incPushDarkColorScheme() { 512 auto ctx = igGetCurrentContext(); 513 ctx.Style.Colors = incDarkModeColors; 514 } 515 516 void incPushLightColorScheme() { 517 auto ctx = igGetCurrentContext(); 518 ctx.Style.Colors = incLightModeColors; 519 } 520 521 void incPopColorScheme() { 522 auto ctx = igGetCurrentContext(); 523 ctx.Style.Colors = isDarkMode ? incDarkModeColors : incLightModeColors; 524 } 525 526 void incSetDarkMode(bool darkMode) { 527 auto style = igGetStyle(); 528 style.Colors = darkMode ? incDarkModeColors : incLightModeColors; 529 530 // Set Dark mode setting 531 incSettingsSet("DarkMode", darkMode); 532 isDarkMode = darkMode; 533 } 534 535 bool incGetDarkMode() { 536 return isDarkMode; 537 } 538 539 /** 540 Gets whether a frame should be processed 541 */ 542 bool incShouldProcess() { 543 return (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) == 0; 544 } 545 546 /** 547 Gets SDL Window Pointer 548 */ 549 SDL_Window* incGetWindowPtr() { 550 return window; 551 } 552 553 void incFinishFileDrag() { 554 files.length = 0; 555 } 556 557 void incBeginLoopNoEv() { 558 // Start the Dear ImGui frame 559 incGLBackendNewFrame(); 560 ImGui_ImplSDL2_NewFrame(); 561 562 // Do our DPI pre-processing 563 igNewFrame(); 564 incGLBackendBeginRender(); 565 566 version(linux) dpUpdate(); 567 568 569 570 if (files.length > 0) { 571 if (igBeginDragDropSource(ImGuiDragDropFlags.SourceExtern)) { 572 igSetDragDropPayload("__PARTS_DROP", &files, files.sizeof); 573 igBeginTooltip(); 574 foreach(file; files) { 575 import creator.widgets.label : incText; 576 incText(file); 577 } 578 igEndTooltip(); 579 igEndDragDropSource(); 580 } 581 } 582 583 // Add docking space 584 viewportDock = igDockSpaceOverViewport(null, ImGuiDockNodeFlags.NoDockingInCentralNode, null); 585 if (!incSettingsCanGet("firstrun_complete")) { 586 incSetDefaultLayout(); 587 incSettingsSet("firstrun_complete", true); 588 } 589 590 incRenderDialogs(); 591 incStatusUpdate(); 592 } 593 594 void incSetDefaultLayout() { 595 import creator.panels; 596 597 igDockBuilderRemoveNodeChildNodes(viewportDock); 598 ImGuiID 599 dockMainID, dockIDNodes, dockIDInspector, dockIDHistory, dockIDParams, 600 dockIDToolSettings, dockIDLoggerAndTextureSlots, dockIDTimeline; 601 602 dockMainID = viewportDock; 603 dockIDNodes = igDockBuilderSplitNode(dockMainID, ImGuiDir.Left, 0.10f, null, &dockMainID); 604 dockIDInspector = igDockBuilderSplitNode(dockIDNodes, ImGuiDir.Down, 0.60f, null, &dockIDNodes); 605 dockIDToolSettings = igDockBuilderSplitNode(dockMainID, ImGuiDir.Right, 0.10f, null, &dockMainID); 606 dockIDHistory = igDockBuilderSplitNode(dockIDToolSettings, ImGuiDir.Down, 0.50f, null, &dockIDToolSettings); 607 dockIDTimeline = igDockBuilderSplitNode(dockMainID, ImGuiDir.Down, 0.15f, null, &dockMainID); 608 dockIDParams = igDockBuilderSplitNode(dockMainID, ImGuiDir.Left, 0.15f, null, &dockMainID); 609 610 igDockBuilderDockWindow("###Nodes", dockIDNodes); 611 igDockBuilderDockWindow("###Inspector", dockIDInspector); 612 igDockBuilderDockWindow("###Tool Settings", dockIDToolSettings); 613 igDockBuilderDockWindow("###History", dockIDHistory); 614 igDockBuilderDockWindow("###Scene", dockIDHistory); 615 debug(InExperimental) igDockBuilderDockWindow("###Tracking", dockIDHistory); 616 igDockBuilderDockWindow("###Timeline", dockIDTimeline); 617 igDockBuilderDockWindow("###Logger", dockIDTimeline); 618 igDockBuilderDockWindow("###Parameters", dockIDParams); 619 igDockBuilderDockWindow("###Texture Slots", dockIDLoggerAndTextureSlots); 620 621 igDockBuilderFinish(viewportDock); 622 } 623 624 /** 625 Begins the Inochi Creator rendering loop 626 */ 627 void incBeginLoop() { 628 SDL_Event event; 629 630 while(SDL_PollEvent(&event)) { 631 switch(event.type) { 632 case SDL_QUIT: 633 incExit(); 634 break; 635 636 case SDL_DROPFILE: 637 files ~= cast(string)event.drop.file.fromStringz; 638 SDL_RaiseWindow(window); 639 break; 640 641 default: 642 incGLBackendProcessEvent(&event); 643 if ( 644 event.type == SDL_WINDOWEVENT && 645 event.window.event == SDL_WINDOWEVENT_CLOSE && 646 event.window.windowID == SDL_GetWindowID(window) 647 ) incExit(); 648 break; 649 } 650 } 651 652 incTaskUpdate(); 653 654 // Begin loop post-event 655 incBeginLoopNoEv(); 656 } 657 658 /** 659 Ends the Inochi Creator rendering loop 660 */ 661 void incEndLoop() { 662 // incGLBackendEndRender(); 663 664 incCleanupDialogs(); 665 666 // Rendering 667 igRender(); 668 glViewport(0, 0, cast(int)(io.DisplaySize.x*incGetUIScale), cast(int)(io.DisplaySize.y*incGetUIScale)); 669 glClearColor(0.5, 0.5, 0.5, 1); 670 glClear(GL_COLOR_BUFFER_BIT); 671 incGLBackendRenderDrawData(igGetDrawData()); 672 673 if (io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) { 674 SDL_Window* currentWindow = SDL_GL_GetCurrentWindow(); 675 SDL_GLContext currentCtx = SDL_GL_GetCurrentContext(); 676 igUpdatePlatformWindows(); 677 igRenderPlatformWindowsDefault(); 678 SDL_GL_MakeCurrent(currentWindow, currentCtx); 679 } 680 681 682 version(InBranding) { 683 import creator.core.egg; 684 incAdaUpdate(); 685 } 686 687 SDL_GL_SwapWindow(window); 688 } 689 690 /** 691 Prints ImGui debug info 692 */ 693 void incDebugImGuiState(string msg, int indent = 0) { 694 debug(imgui) { 695 static int currentIndent = 0; 696 697 string flag = " "; 698 if (indent > 0) { 699 currentIndent += indent; 700 flag = ">>"; 701 } else if (indent < 0) { 702 flag = "<<"; 703 } 704 705 //auto g = igGetCurrentContext(); 706 auto win = igGetCurrentWindow(); 707 writefln( 708 "%s%s%s [%s]", ' '.repeat(currentIndent * 2), flag, msg, 709 to!string(win.Name) 710 ); 711 712 if (indent < 0) { 713 currentIndent += indent; 714 if (currentIndent < 0) { 715 debug writeln("ERROR: dedented too far!"); 716 currentIndent = 0; 717 } 718 } 719 } 720 } 721 722 /** 723 Gets whether Inochi Creator has requested the app to close 724 */ 725 bool incIsCloseRequested() { 726 return done; 727 } 728 729 /** 730 Exit Inochi Creator 731 */ 732 void incExit() { 733 done = true; 734 735 int w, h; 736 SDL_WindowFlags flags; 737 flags = SDL_GetWindowFlags(window); 738 SDL_GetWindowSize(window, &w, &h); 739 incSettingsSet("WinW", w); 740 incSettingsSet("WinH", h); 741 incSettingsSet!bool("WinMax", (flags & SDL_WINDOW_MAXIMIZED) > 0); 742 } 743 744 /** 745 Main font 746 */ 747 ImFont* incMainFont() { 748 return mainFont; 749 } 750 751 version (InBranding) { 752 /** 753 Gets the Inochi2D Logo 754 */ 755 Texture incGetLogo() { 756 return incLogo; 757 } 758 759 /** 760 Gets the Ada texture 761 */ 762 Texture incGetAda() { 763 return incAda; 764 } 765 766 Texture incGetLogoI2D() { 767 return incLogoI2D; 768 } 769 } 770 771 /** 772 Gets the grid texture 773 */ 774 Texture incGetGrid() { 775 return incGrid; 776 } 777 778 void incHandleShortcuts() { 779 auto io = igGetIO(); 780 781 if (incShortcut("Ctrl+Shift+Z", true)) { 782 incActionRedo(); 783 } else if (incShortcut("Ctrl+Z", true)) { 784 incActionUndo(); 785 } 786 }