2024-03-27 00:02:03 +00:00
2024-05-22 14:39:37 +00:00
# define IMGUI_DEFINE_MATH_OPERATORS
2024-04-06 02:03:22 +00:00
# include <SDL.h>
2024-03-27 00:02:03 +00:00
# include "sys_imgui.h"
2024-04-06 02:03:22 +00:00
# ifdef D3_SDL_X11
# include <dlfcn.h>
# include <SDL_syswm.h>
//char *XGetDefault(Display* display, const char* program, const char* option)
typedef char * ( * MY_XGETDEFAULTFUN ) ( Display * , const char * , const char * ) ;
# endif
2024-03-27 00:02:03 +00:00
# include "../libs/imgui/backends/imgui_impl_opengl2.h"
# include "../libs/imgui/backends/imgui_impl_sdl2.h"
# include "framework/Common.h"
2024-05-22 14:39:37 +00:00
# include "framework/KeyInput.h"
2024-03-27 00:02:03 +00:00
# include "renderer/qgl.h"
2024-03-29 00:16:30 +00:00
# include "renderer/tr_local.h" // glconfig
2024-03-27 00:02:03 +00:00
2024-04-05 04:33:33 +00:00
extern void Com_DrawDhewm3SettingsMenu ( ) ; // in framework/dhewm3SettingsMenu.cpp
2024-03-27 00:02:03 +00:00
2024-04-19 10:30:37 +00:00
static idCVar imgui_scale ( " imgui_scale " , " -1.0 " , CVAR_SYSTEM | CVAR_FLOAT | CVAR_ARCHIVE , " factor to scale ImGUI menus by (-1: auto) " ) ; // TODO: limit values?
2024-05-24 02:00:19 +00:00
idCVar imgui_style ( " imgui_style " , " 0 " , CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE , " Which ImGui style to use. 0: Dhewm3 theme, 1: Default ImGui theme, 2: User theme " , 0.0f , 2.0f ) ;
2024-03-27 00:02:03 +00:00
namespace D3 {
namespace ImGuiHooks {
2024-04-19 10:30:37 +00:00
# include "proggyvector_font.h"
2024-04-06 02:03:22 +00:00
static SDL_Window * sdlWindow = NULL ;
2024-03-27 00:02:03 +00:00
ImGuiContext * imguiCtx = NULL ;
static bool haveNewFrame = false ;
2024-04-05 04:33:33 +00:00
static int openImguiWindows = 0 ; // or-ed enum D3ImGuiWindow values
2024-05-24 02:00:19 +00:00
static ImGuiStyle userStyle ;
2024-05-22 14:39:37 +00:00
// was there a key down or button (mouse/gamepad) down event this frame?
// used to make the warning overlay disappear
static bool hadKeyDownEvent = false ;
static idStr warningOverlayText ;
static double warningOverlayStartTime = - 100.0 ;
static ImVec2 warningOverlayStartPos ;
2024-05-24 02:00:19 +00:00
bool WriteStyle ( const ImGuiStyle & s , const char * filename ) ;
bool ReadStyle ( ImGuiStyle & s , const char * filename ) ;
idStr GetUserStyleFilename ( )
{
// TODO: put this into the config dir
return idStr ( " user.imstyle " ) ;
}
2024-05-22 14:39:37 +00:00
static void UpdateWarningOverlay ( )
{
double timeNow = ImGui : : GetTime ( ) ;
if ( timeNow - warningOverlayStartTime > 4.0f ) {
warningOverlayStartTime = - 100.0f ;
return ;
}
// also hide if a key was pressed or maybe even if the mouse was moved (too much)
ImVec2 mdv = ImGui : : GetMousePos ( ) - warningOverlayStartPos ; // Mouse Delta Vector
float mouseDelta = sqrtf ( mdv . x * mdv . x + mdv . y * mdv . y ) ;
const float fontSize = ImGui : : GetFontSize ( ) ;
if ( mouseDelta > fontSize * 4.0f | | hadKeyDownEvent ) {
warningOverlayStartTime = - 100.0f ;
return ;
}
ImGui : : SetNextWindowPos ( ImGui : : GetMainViewport ( ) - > GetCenter ( ) , ImGuiCond_Always , ImVec2 ( 0.5f , 0.5f ) ) ;
ImGui : : PushStyleColor ( ImGuiCol_WindowBg , ImVec4 ( 1.0f , 0.4f , 0.4f , 0.4f ) ) ;
float padSize = fontSize * 2.0f ;
ImGui : : PushStyleVar ( ImGuiStyleVar_WindowPadding , ImVec2 ( padSize , padSize ) ) ;
int winFlags = ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize ;
ImGui : : Begin ( " WarningOverlay " , NULL , winFlags ) ;
ImDrawList * drawList = ImGui : : GetWindowDrawList ( ) ;
ImVec2 points [ ] = {
{ 0 , 40 } , { 40 , 40 } , { 20 , 0 } , // triangle
{ 20 , 12 } , { 20 , 28 } , // line
{ 20 , 33 } // dot
} ;
float iconScale = 1.0f ; // TODO: global scale also used for fontsize
ImVec2 offset = ImGui : : GetWindowPos ( ) + ImVec2 ( fontSize , fontSize ) ;
for ( ImVec2 & v : points ) {
v . x = roundf ( v . x * iconScale ) ;
v . y = roundf ( v . y * iconScale ) ;
v + = offset ;
}
ImU32 color = ImGui : : GetColorU32 ( ImVec4 ( 0.1f , 0.1f , 0.1f , 1.0f ) ) ;
drawList - > AddTriangle ( points [ 0 ] , points [ 1 ] , points [ 2 ] , color , roundf ( iconScale * 4.0f ) ) ;
drawList - > AddPolyline ( points + 3 , 2 , color , 0 , roundf ( iconScale * 3.0f ) ) ;
float dotRadius = 2.0f * iconScale ;
drawList - > AddEllipseFilled ( points [ 5 ] , ImVec2 ( dotRadius , dotRadius ) , color , 0 , 6 ) ;
ImGui : : Indent ( 40.0f * iconScale ) ;
ImGui : : TextUnformatted ( warningOverlayText . c_str ( ) ) ;
ImGui : : End ( ) ;
ImGui : : PopStyleVar ( ) ; // WindowPadding
ImGui : : PopStyleColor ( ) ; // WindowBg
}
void ShowWarningOverlay ( const char * text )
{
warningOverlayText = text ;
warningOverlayStartTime = ImGui : : GetTime ( ) ;
warningOverlayStartPos = ImGui : : GetMousePos ( ) ;
}
2024-04-19 10:30:37 +00:00
static float GetDefaultDPI ( )
2024-04-06 02:03:22 +00:00
{
SDL_Window * win = sdlWindow ;
float dpi = - 1.0f ;
# ifdef D3_SDL_X11
SDL_SysWMinfo wmInfo = { } ;
SDL_VERSION ( & wmInfo . version )
if ( SDL_GetWindowWMInfo ( win , & wmInfo ) & & wmInfo . subsystem = = SDL_SYSWM_X11 ) {
Display * display = wmInfo . info . x11 . display ;
static void * libX11 = NULL ;
if ( libX11 = = NULL ) {
libX11 = dlopen ( " libX11.so.6 " , RTLD_LAZY ) ;
}
if ( libX11 = = NULL ) {
libX11 = dlopen ( " libX11.so " , RTLD_LAZY ) ;
}
if ( libX11 ! = NULL ) {
MY_XGETDEFAULTFUN my_xgetdefault = ( MY_XGETDEFAULTFUN ) dlsym ( libX11 , " XGetDefault " ) ;
if ( my_xgetdefault ! = NULL ) {
//char *XGetDefault(Display* display, const char* program, const char* option)
const char * dpiStr = my_xgetdefault ( display , " Xft " , " dpi " ) ;
printf ( " XX dpistr = '%s' \n " , dpiStr ) ;
if ( dpiStr ! = NULL ) {
dpi = atof ( dpiStr ) ;
}
}
}
}
if ( dpi = = - 1.0f )
# endif
{
int winIdx = SDL_GetWindowDisplayIndex ( win ) ;
if ( winIdx > = 0 ) {
SDL_GetDisplayDPI ( winIdx , NULL , & dpi , NULL ) ;
}
}
return dpi ;
}
2024-04-19 10:30:37 +00:00
static float GetDefaultScale ( )
{
float ret = GetDefaultDPI ( ) / 96.0f ;
ret = round ( ret * 2.0 ) * 0.5 ; // round to .0 or .5
return ret ;
}
float GetScale ( )
{
float ret = imgui_scale . GetFloat ( ) ;
if ( ret < 0.0f ) {
ret = GetDefaultScale ( ) ;
}
return ret ;
}
void SetScale ( float scale )
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
float realScale = ( scale < 0.0f ) ? GetDefaultScale ( ) : scale ;
io . FontGlobalScale = realScale ;
// TODO: could instead set fontsize to 18.0f * scale
// (io.Fonts->ClearFonts() and then add again with new size - but must be done before NewFrame() / after EndFrame())
imgui_scale . SetFloat ( scale ) ;
}
static bool imgui_initialized = false ;
2024-03-27 00:02:03 +00:00
// using void* instead of SDL_Window and SDL_GLContext to avoid dragging SDL headers into sys_imgui.h
2024-04-06 02:03:22 +00:00
bool Init ( void * _sdlWindow , void * sdlGlContext )
2024-03-27 00:02:03 +00:00
{
common - > Printf ( " Initializing ImGui \n " ) ;
2024-04-19 10:30:37 +00:00
sdlWindow = ( SDL_Window * ) _sdlWindow ;
2024-04-06 02:03:22 +00:00
2024-03-27 00:02:03 +00:00
// Setup Dear ImGui context
IMGUI_CHECKVERSION ( ) ;
imguiCtx = ImGui : : CreateContext ( ) ;
2024-03-29 00:16:30 +00:00
if ( imguiCtx = = NULL ) {
common - > Warning ( " Failed to create ImGui Context! \n " ) ;
2024-03-27 00:02:03 +00:00
return false ;
}
ImGuiIO & io = ImGui : : GetIO ( ) ; ( void ) io ;
io . ConfigFlags | = ImGuiConfigFlags_NavEnableKeyboard ; // Enable Keyboard Controls
io . ConfigFlags | = ImGuiConfigFlags_NavEnableGamepad ; // Enable Gamepad Controls
2024-05-24 02:00:19 +00:00
SetImGuiStyle ( Style : : Dhewm3 ) ;
userStyle = ImGui : : GetStyle ( ) ; // set dhewm3 style as default, in case the user style is missing values
if ( ReadStyle ( userStyle , GetUserStyleFilename ( ) ) & & imgui_style . GetInteger ( ) = = 2 ) {
ImGui : : GetStyle ( ) = userStyle ;
} else if ( imgui_style . GetInteger ( ) = = 1 ) {
ImGui : : GetStyle ( ) = ImGuiStyle ( ) ;
ImGui : : StyleColorsDark ( ) ;
}
2024-04-19 10:30:37 +00:00
ImFontConfig fontCfg ;
strcpy ( fontCfg . Name , " ProggyVector " ) ;
ImFont * font = io . Fonts - > AddFontFromMemoryCompressedTTF ( ProggyVector_compressed_data , ProggyVector_compressed_size , 18.0f , nullptr ) ;
SetScale ( GetScale ( ) ) ;
2024-03-27 00:02:03 +00:00
// Setup Platform/Renderer backends
2024-04-19 10:30:37 +00:00
if ( ! ImGui_ImplSDL2_InitForOpenGL ( sdlWindow , sdlGlContext ) ) {
2024-03-29 00:16:30 +00:00
ImGui : : DestroyContext ( imguiCtx ) ;
2024-03-27 00:02:03 +00:00
imguiCtx = NULL ;
2024-03-29 00:16:30 +00:00
common - > Warning ( " Failed to initialize ImGui SDL platform backend! \n " ) ;
2024-03-27 00:02:03 +00:00
return false ;
}
2024-03-29 00:16:30 +00:00
if ( ! ImGui_ImplOpenGL2_Init ( ) ) {
2024-03-27 00:02:03 +00:00
ImGui_ImplSDL2_Shutdown ( ) ;
2024-03-29 00:16:30 +00:00
ImGui : : DestroyContext ( imguiCtx ) ;
2024-03-27 00:02:03 +00:00
imguiCtx = NULL ;
2024-03-29 00:16:30 +00:00
common - > Warning ( " Failed to initialize ImGui OpenGL renderer backend! \n " ) ;
2024-03-27 00:02:03 +00:00
return false ;
}
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
2024-05-23 00:11:54 +00:00
const char * f10bind = idKeyInput : : GetBinding ( K_F10 ) ;
if ( f10bind & & f10bind [ 0 ] ! = ' \0 ' ) {
if ( idStr : : Icmp ( f10bind , " dhewm3Settings " ) ! = 0 ) {
// if F10 is already bound, but not to dhewm3Settings, show a message
common - > Printf ( " ... the F10 key is already bound to '%s', otherwise it could be used to open the dhewm3 Settings Menu \n " , f10bind ) ;
}
} else {
idKeyInput : : SetBinding ( K_F10 , " dhewm3Settings " ) ;
}
2024-04-19 10:30:37 +00:00
imgui_initialized = true ;
2024-03-27 00:02:03 +00:00
return true ;
}
void Shutdown ( )
{
2024-04-19 10:30:37 +00:00
if ( imgui_initialized ) {
common - > Printf ( " Shutting down ImGui \n " ) ;
2024-05-24 02:00:19 +00:00
2024-04-19 10:30:37 +00:00
// TODO: only if init was successful!
ImGui_ImplOpenGL2_Shutdown ( ) ;
ImGui_ImplSDL2_Shutdown ( ) ;
ImGui : : DestroyContext ( imguiCtx ) ;
imgui_initialized = false ;
}
2024-03-27 00:02:03 +00:00
}
2024-04-05 04:33:33 +00:00
// NewFrame() is called once per D3 frame, after all events have been gotten
// => ProcessEvent() has already been called (probably multiple times)
2024-03-27 00:02:03 +00:00
void NewFrame ( )
{
2024-04-05 04:33:33 +00:00
if ( openImguiWindows = = 0 )
return ;
2024-03-27 00:02:03 +00:00
// Start the Dear ImGui frame
ImGui_ImplOpenGL2_NewFrame ( ) ;
ImGui_ImplSDL2_NewFrame ( ) ;
ImGui : : NewFrame ( ) ;
haveNewFrame = true ;
2024-05-22 14:39:37 +00:00
UpdateWarningOverlay ( ) ;
2024-04-05 04:33:33 +00:00
if ( openImguiWindows & D3_ImGuiWin_Settings ) {
Com_DrawDhewm3SettingsMenu ( ) ;
2024-03-27 00:02:03 +00:00
}
2024-04-05 04:33:33 +00:00
if ( openImguiWindows & D3_ImGuiWin_Demo ) {
bool show_demo_window = true ;
ImGui : : ShowDemoWindow ( & show_demo_window ) ;
if ( ! show_demo_window )
CloseWindow ( D3_ImGuiWin_Demo ) ;
2024-03-27 00:02:03 +00:00
}
}
2024-05-22 14:39:37 +00:00
bool keybindModeEnabled = false ;
2024-04-05 04:33:33 +00:00
// called with every SDL event by Sys_GetEvent()
// returns true if ImGui has handled the event (so it shouldn't be handled by D3)
2024-03-27 00:02:03 +00:00
bool ProcessEvent ( const void * sdlEvent )
{
2024-04-05 04:33:33 +00:00
if ( openImguiWindows = = 0 )
2024-04-19 10:30:37 +00:00
return false ;
2024-04-05 04:33:33 +00:00
2024-03-29 00:16:30 +00:00
const SDL_Event * ev = ( const SDL_Event * ) sdlEvent ;
// ImGui_ImplSDL2_ProcessEvent() doc says:
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
2024-05-22 14:39:37 +00:00
bool imguiUsedEvent = ImGui_ImplSDL2_ProcessEvent ( ev ) ;
if ( keybindModeEnabled ) {
// in keybind mode, all input events are passed to Doom3 so it can translate them
// to internal events and we can access and use them to create a new binding
return false ;
}
if ( ImGui : : IsWindowFocused ( ImGuiFocusedFlags_AnyWindow )
& & ( ev - > type = = SDL_CONTROLLERAXISMOTION | | ev - > type = = SDL_CONTROLLERBUTTONDOWN ) ) {
// don't pass on controller axis events to avoid moving the mouse cursor
// and controller button events to avoid emulating mouse clicks
return true ;
}
switch ( ev - > type ) {
// Hack: detect if any key was pressed to close the warning overlay
case SDL_CONTROLLERBUTTONDOWN :
case SDL_MOUSEWHEEL :
case SDL_MOUSEBUTTONDOWN :
case SDL_KEYDOWN :
// TODO: controller trigger?
hadKeyDownEvent = true ;
}
if ( imguiUsedEvent ) {
2024-03-29 00:16:30 +00:00
ImGuiIO & io = ImGui : : GetIO ( ) ;
2024-05-22 14:39:37 +00:00
2024-03-29 00:16:30 +00:00
if ( io . WantCaptureMouse ) {
switch ( ev - > type ) {
case SDL_MOUSEMOTION :
case SDL_MOUSEWHEEL :
case SDL_MOUSEBUTTONDOWN :
2024-05-22 14:39:37 +00:00
// Note: still pass button up events to the engine, so if they were pressed down
// before an imgui window got focus they don't remain pressed indefinitely
//case SDL_MOUSEBUTTONUP:
2024-03-29 00:16:30 +00:00
return true ;
}
}
if ( io . WantCaptureKeyboard ) {
switch ( ev - > type ) {
case SDL_TEXTINPUT :
return true ;
2024-05-22 14:39:37 +00:00
case SDL_KEYDOWN :
//case SDL_KEYUP: NOTE: see above why key up events are passed to the engine
if ( ev - > key . keysym . sym < SDLK_F1 | | ev - > key . keysym . sym > SDLK_F12 ) {
// F1 - F12 are passed to the engine so its shortcuts
// (like quickload or screenshot) still work
// Doom3's menu does the same
return true ;
}
2024-03-29 00:16:30 +00:00
}
}
2024-05-22 14:39:37 +00:00
2024-03-29 00:16:30 +00:00
}
2024-05-22 14:39:37 +00:00
2024-03-29 00:16:30 +00:00
return false ;
2024-03-27 00:02:03 +00:00
}
2024-05-22 14:39:37 +00:00
void SetKeyBindMode ( bool enable )
{
keybindModeEnabled = enable ;
// make sure no keys are registered as down, neither when entering nor when exiting keybind mode
idKeyInput : : ClearStates ( ) ;
}
2024-03-27 00:02:03 +00:00
void EndFrame ( )
{
2024-04-05 04:33:33 +00:00
if ( openImguiWindows = = 0 & & ! haveNewFrame )
return ;
2024-03-27 00:02:03 +00:00
// I think this can happen if we're not coming from idCommon::Frame() but screenshot or sth
2024-03-29 00:16:30 +00:00
if ( ! haveNewFrame ) {
2024-03-27 00:02:03 +00:00
NewFrame ( ) ;
}
haveNewFrame = false ;
ImGui : : Render ( ) ;
2024-03-29 00:16:30 +00:00
// Doom3 uses the OpenGL ARB shader extensions, for most things it renders.
// disable those shaders, the OpenGL classic integration of ImGui doesn't use shaders
2024-03-27 00:02:03 +00:00
qglDisable ( GL_VERTEX_PROGRAM_ARB ) ;
qglDisable ( GL_FRAGMENT_PROGRAM_ARB ) ;
2024-03-29 00:16:30 +00:00
// Doom3 uses OpenGL's ARB_vertex_buffer_object extension to use VBOs on the GPU
// as buffers for glDrawElements() (instead of passing userspace buffers to that function)
// ImGui however uses userspace buffers, so remember the currently bound VBO
// and unbind it (after drawing, bind it again)
GLint curArrayBuffer = 0 ;
if ( glConfig . ARBVertexBufferObjectAvailable ) {
qglGetIntegerv ( GL_ARRAY_BUFFER_BINDING_ARB , & curArrayBuffer ) ;
qglBindBufferARB ( GL_ARRAY_BUFFER_ARB , 0 ) ;
}
2024-03-27 00:02:03 +00:00
2024-03-29 00:16:30 +00:00
// disable all texture units, ImGui_ImplOpenGL2_RenderDrawData() will enable texture 0
// and bind its own textures to it as needed
for ( int i = glConfig . maxTextureUnits - 1 ; i > = 0 ; i - - ) {
GL_SelectTexture ( i ) ;
qglDisable ( GL_TEXTURE_2D ) ;
if ( glConfig . texture3DAvailable ) {
qglDisable ( GL_TEXTURE_3D ) ;
}
if ( glConfig . cubeMapAvailable ) {
qglDisable ( GL_TEXTURE_CUBE_MAP_EXT ) ;
}
}
2024-03-27 00:02:03 +00:00
2024-03-29 00:16:30 +00:00
ImGui_ImplOpenGL2_RenderDrawData ( ImGui : : GetDrawData ( ) ) ;
2024-03-27 00:02:03 +00:00
2024-03-29 00:16:30 +00:00
if ( curArrayBuffer ! = 0 ) {
2024-03-27 00:02:03 +00:00
qglBindBufferARB ( GL_ARRAY_BUFFER_ARB , curArrayBuffer ) ;
}
2024-05-22 14:39:37 +00:00
// reset this at the end of each frame, will be set again by ProcessEvent()
if ( hadKeyDownEvent ) {
hadKeyDownEvent = false ;
}
2024-03-27 00:02:03 +00:00
}
2024-04-05 04:33:33 +00:00
void OpenWindow ( D3ImGuiWindow win )
{
openImguiWindows | = win ;
}
void CloseWindow ( D3ImGuiWindow win )
{
openImguiWindows & = ~ win ;
}
2024-04-19 10:30:37 +00:00
int GetOpenWindowsMask ( )
{
return openImguiWindows ;
}
2024-05-24 02:00:19 +00:00
void SetImGuiStyle ( Style d3style )
{
ImGuiStyle & style = ImGui : : GetStyle ( ) ;
if ( d3style = = Style : : Dhewm3 ) {
style = ImGuiStyle ( ) ; // start with default style
// make it look a bit nicer with rounded edges
style . WindowRounding = 2.0f ;
style . FrameRounding = 3.0f ;
//style.ChildRounding = 6.0f;
style . ScrollbarRounding = 8.0f ;
style . GrabRounding = 1.0f ;
style . PopupRounding = 2.0f ;
SetDhewm3StyleColors ( ) ;
} else if ( d3style = = Style : : User ) {
style = userStyle ;
} else {
assert ( d3style = = Style : : ImGui_Default & & " invalid/unknown style " ) ;
style = ImGuiStyle ( ) ;
ImGui : : StyleColorsDark ( ) ;
}
}
void SetDhewm3StyleColors ( )
{
ImGui : : StyleColorsDark ( ) ;
ImGuiStyle & style = ImGui : : GetStyle ( ) ;
ImVec4 * colors = style . Colors ;
colors [ ImGuiCol_TitleBg ] = ImVec4 ( 0.28f , 0.36f , 0.48f , 0.88f ) ;
colors [ ImGuiCol_TabHovered ] = ImVec4 ( 0.42f , 0.69f , 1.00f , 0.80f ) ;
colors [ ImGuiCol_TabActive ] = ImVec4 ( 0.24f , 0.51f , 0.83f , 1.00f ) ;
}
void SetUserStyleColors ( )
{
ImGuiStyle & style = ImGui : : GetStyle ( ) ;
for ( int i = 0 ; i < ImGuiCol_COUNT ; + + i ) {
style . Colors [ i ] = userStyle . Colors [ i ] ;
}
}
bool WriteUserStyle ( )
{
userStyle = ImGui : : GetStyle ( ) ;
if ( ! WriteStyle ( ImGui : : GetStyle ( ) , GetUserStyleFilename ( ) ) ) {
common - > Warning ( " Couldn't write ImGui userstyle! \n " ) ;
return false ;
}
return true ;
}
// Note: The trick I'm using with these #defines below is called "X Macro"
// see https://en.wikipedia.org/wiki/X_macro (except I'm not calling the "entries" X)
# define D3_IMSTYLE_ATTRS \
D3_IMATTR_FLOAT ( Alpha ) \
D3_IMATTR_FLOAT ( DisabledAlpha ) \
D3_IMATTR_VEC2 ( WindowPadding ) \
D3_IMATTR_FLOAT ( WindowRounding ) \
D3_IMATTR_FLOAT ( WindowBorderSize ) \
D3_IMATTR_VEC2 ( WindowMinSize ) \
D3_IMATTR_VEC2 ( WindowTitleAlign ) \
D3_IMATTR_INT ( WindowMenuButtonPosition ) \
D3_IMATTR_FLOAT ( ChildRounding ) \
D3_IMATTR_FLOAT ( ChildBorderSize ) \
D3_IMATTR_FLOAT ( PopupRounding ) \
D3_IMATTR_FLOAT ( PopupBorderSize ) \
D3_IMATTR_VEC2 ( FramePadding ) \
D3_IMATTR_FLOAT ( FrameRounding ) \
D3_IMATTR_FLOAT ( FrameBorderSize ) \
D3_IMATTR_VEC2 ( ItemSpacing ) \
D3_IMATTR_VEC2 ( ItemInnerSpacing ) \
D3_IMATTR_VEC2 ( CellPadding ) \
D3_IMATTR_VEC2 ( TouchExtraPadding ) \
D3_IMATTR_FLOAT ( IndentSpacing ) \
D3_IMATTR_FLOAT ( ColumnsMinSpacing ) \
D3_IMATTR_FLOAT ( ScrollbarSize ) \
D3_IMATTR_FLOAT ( ScrollbarRounding ) \
D3_IMATTR_FLOAT ( GrabMinSize ) \
D3_IMATTR_FLOAT ( GrabRounding ) \
D3_IMATTR_FLOAT ( LogSliderDeadzone ) \
D3_IMATTR_FLOAT ( TabRounding ) \
D3_IMATTR_FLOAT ( TabBorderSize ) \
D3_IMATTR_FLOAT ( TabMinWidthForCloseButton ) \
D3_IMATTR_FLOAT ( TabBarBorderSize ) \
D3_IMATTR_FLOAT ( TableAngledHeadersAngle ) \
D3_IMATTR_VEC2 ( TableAngledHeadersTextAlign ) \
D3_IMATTR_INT ( ColorButtonPosition ) \
D3_IMATTR_VEC2 ( ButtonTextAlign ) \
D3_IMATTR_VEC2 ( SelectableTextAlign ) \
D3_IMATTR_FLOAT ( SeparatorTextBorderSize ) \
D3_IMATTR_VEC2 ( SeparatorTextAlign ) \
D3_IMATTR_VEC2 ( SeparatorTextPadding ) \
D3_IMATTR_VEC2 ( DisplayWindowPadding ) \
D3_IMATTR_VEC2 ( DisplaySafeAreaPadding ) \
D3_IMATTR_FLOAT ( MouseCursorScale ) \
D3_IMATTR_BOOL ( AntiAliasedLines ) \
D3_IMATTR_BOOL ( AntiAliasedLinesUseTex ) \
D3_IMATTR_BOOL ( AntiAliasedFill ) \
D3_IMATTR_FLOAT ( CurveTessellationTol ) \
D3_IMATTR_FLOAT ( CircleTessellationMaxError )
# define D3_IMSTYLE_BEHAVIORS \
D3_IMATTR_FLOAT ( HoverStationaryDelay ) \
D3_IMATTR_FLOAT ( HoverDelayShort ) \
D3_IMATTR_FLOAT ( HoverDelayNormal ) \
D3_IMATTR_INT ( HoverFlagsForTooltipMouse ) \
D3_IMATTR_INT ( HoverFlagsForTooltipNav )
# define D3_IMSTYLE_COLORS \
D3_IMSTYLE_COLOR ( Text ) \
D3_IMSTYLE_COLOR ( TextDisabled ) \
D3_IMSTYLE_COLOR ( WindowBg ) \
D3_IMSTYLE_COLOR ( ChildBg ) \
D3_IMSTYLE_COLOR ( PopupBg ) \
D3_IMSTYLE_COLOR ( Border ) \
D3_IMSTYLE_COLOR ( BorderShadow ) \
D3_IMSTYLE_COLOR ( FrameBg ) \
D3_IMSTYLE_COLOR ( FrameBgHovered ) \
D3_IMSTYLE_COLOR ( FrameBgActive ) \
D3_IMSTYLE_COLOR ( TitleBg ) \
D3_IMSTYLE_COLOR ( TitleBgActive ) \
D3_IMSTYLE_COLOR ( TitleBgCollapsed ) \
D3_IMSTYLE_COLOR ( MenuBarBg ) \
D3_IMSTYLE_COLOR ( ScrollbarBg ) \
D3_IMSTYLE_COLOR ( ScrollbarGrab ) \
D3_IMSTYLE_COLOR ( ScrollbarGrabHovered ) \
D3_IMSTYLE_COLOR ( ScrollbarGrabActive ) \
D3_IMSTYLE_COLOR ( CheckMark ) \
D3_IMSTYLE_COLOR ( SliderGrab ) \
D3_IMSTYLE_COLOR ( SliderGrabActive ) \
D3_IMSTYLE_COLOR ( Button ) \
D3_IMSTYLE_COLOR ( ButtonHovered ) \
D3_IMSTYLE_COLOR ( ButtonActive ) \
D3_IMSTYLE_COLOR ( Header ) \
D3_IMSTYLE_COLOR ( HeaderHovered ) \
D3_IMSTYLE_COLOR ( HeaderActive ) \
D3_IMSTYLE_COLOR ( Separator ) \
D3_IMSTYLE_COLOR ( SeparatorHovered ) \
D3_IMSTYLE_COLOR ( SeparatorActive ) \
D3_IMSTYLE_COLOR ( ResizeGrip ) \
D3_IMSTYLE_COLOR ( ResizeGripHovered ) \
D3_IMSTYLE_COLOR ( ResizeGripActive ) \
D3_IMSTYLE_COLOR ( Tab ) \
D3_IMSTYLE_COLOR ( TabHovered ) \
D3_IMSTYLE_COLOR ( TabActive ) \
D3_IMSTYLE_COLOR ( TabUnfocused ) \
D3_IMSTYLE_COLOR ( TabUnfocusedActive ) \
D3_IMSTYLE_COLOR ( PlotLines ) \
D3_IMSTYLE_COLOR ( PlotLinesHovered ) \
D3_IMSTYLE_COLOR ( PlotHistogram ) \
D3_IMSTYLE_COLOR ( PlotHistogramHovered ) \
D3_IMSTYLE_COLOR ( TableHeaderBg ) \
D3_IMSTYLE_COLOR ( TableBorderStrong ) \
D3_IMSTYLE_COLOR ( TableBorderLight ) \
D3_IMSTYLE_COLOR ( TableRowBg ) \
D3_IMSTYLE_COLOR ( TableRowBgAlt ) \
D3_IMSTYLE_COLOR ( TextSelectedBg ) \
D3_IMSTYLE_COLOR ( DragDropTarget ) \
D3_IMSTYLE_COLOR ( NavHighlight ) \
D3_IMSTYLE_COLOR ( NavWindowingHighlight ) \
D3_IMSTYLE_COLOR ( NavWindowingDimBg ) \
D3_IMSTYLE_COLOR ( ModalWindowDimBg )
bool WriteStyle ( const ImGuiStyle & s , const char * filename ) {
FILE * f = fopen ( filename , " w " ) ; // TODO: "wt" on Windows?
if ( f = = nullptr ) {
return false ;
}
fprintf ( f , " [style] \n " ) ;
# define D3_IMATTR_FLOAT( NAME ) \
fprintf ( f , # NAME " = %g \n " , s . NAME ) ;
# define D3_IMATTR_VEC2( NAME ) \
fprintf ( f , # NAME " = %g, %g \n " , s . NAME . x , s . NAME . y ) ;
# define D3_IMATTR_INT( NAME ) \
fprintf ( f , # NAME " = %d \n " , s . NAME ) ;
# define D3_IMATTR_BOOL( NAME ) \
fprintf ( f , # NAME " = %d \n " , ( int ) ( s . NAME ) ) ;
// this (together with the D3_IMATTR_* defines in the previous lines)
// extends the D3_IMSTYLE_ATTRS table to
// fprintf( f, "Alpha = %f\n", s . Alpha );
// fprintf( f, "DisabledAlpha = %f\n", s . DisabledAlpha );
// fprintf( f, "WindowPadding = %f %f\n", s . WindowPadding . x, s . WindowPadding . y );
// etc
D3_IMSTYLE_ATTRS
fprintf ( f , " \n [behaviors] \n " ) ;
// same for behaviors
D3_IMSTYLE_BEHAVIORS
# undef D3_IMATTR_FLOAT
# undef D3_IMATTR_VEC2
# undef D3_IMATTR_INT
# undef D3_IMATTR_BOOL
fprintf ( f , " \n [colors] \n " ) ;
# define D3_IMSTYLE_COLOR( NAME ) { \
const ImVec4 & c = s . Colors [ ImGuiCol_ # # NAME ] ; \
fprintf ( f , # NAME " = %g, %g, %g, %g \n " , c . x , c . y , c . z , c . w ) ; \
}
// this turns into
// { const ImVec4& c = s.Colors[ ImGuiCol_Text ]; fprintf( f, "Text = %f %f %f %f\n", c.x, c.y, c.z, c.w ); }
// { const ImVec4& c = s.Colors[ ImGuiCol_TextDisabled ]; fprintf( f, "TextDisabled = %f %f %f %f\n", c.x, c.y, c.z, c.w ); }
// etc
D3_IMSTYLE_COLORS
# undef D3_IMSTYLE_COLOR
fprintf ( f , " \n " ) ;
fflush ( f ) ;
fclose ( f ) ;
return true ;
}
static inline char * skipWhitespace ( const char * s ) {
while ( * s = = ' ' | | * s = = ' \t ' )
+ + s ;
return ( char * ) s ;
}
# define D3_IMATTR_FLOAT( NAME ) \
if ( sscanf ( line , # NAME " = %f " , & f1 ) = = 1 ) { \
s . NAME = f1 ; \
return ; \
}
# define D3_IMATTR_VEC2( NAME ) \
if ( sscanf ( line , # NAME " = %f , %f " , & f1 , & f2 ) = = 2 ) { \
s . NAME . x = f1 ; s . NAME . y = f2 ; \
return ; \
}
# define D3_IMATTR_INT( NAME ) \
if ( sscanf ( line , # NAME " = %d " , & i ) = = 1 ) { \
s . NAME = i ; \
return ; \
}
# define D3_IMATTR_BOOL( NAME ) \
if ( sscanf ( line , # NAME " = %d " , & i ) = = 1 ) { \
s . NAME = ( i ! = 0 ) ; \
return ; \
}
static void parseStyleLine ( ImGuiStyle & s , const char * line )
{
float f1 = 0 , f2 = 0 ;
int i = 0 ;
D3_IMSTYLE_ATTRS
common - > Warning ( " Invalid line in ImGui style under [style] section: '%s' \n " , line ) ;
}
static void parseBehaviorLine ( ImGuiStyle & s , const char * line )
{
float f1 = 0 , f2 = 0 ;
( void ) f2 ; // currently unused in behavior section
int i = 0 ;
D3_IMSTYLE_BEHAVIORS
common - > Warning ( " Invalid line in ImGui style under [behaviors] section: '%s' \n " , line ) ;
}
# undef D3_IMATTR_FLOAT
# undef D3_IMATTR_VEC2
# undef D3_IMATTR_INT
# undef D3_IMATTR_BOOL
static void parseColorLine ( ImGuiStyle & s , const char * line )
{
ImVec4 c ;
# define D3_IMSTYLE_COLOR( NAME ) \
if ( sscanf ( line , # NAME " = %f , %f , %f , %f " , & c . x , & c . y , & c . z , & c . w ) = = 4 ) { \
s . Colors [ ImGuiCol_ # # NAME ] = c ; \
return ; \
}
D3_IMSTYLE_COLORS
# undef D3_IMSTYLE_COLOR
common - > Warning ( " Invalid line in ImGui style under [colors] section: '%s' \n " , line ) ;
}
bool ReadStyle ( ImGuiStyle & s , const char * filename )
{
FILE * f = fopen ( filename , " r " ) ; // TODO: "rt" on Windows?
if ( f = = nullptr ) {
return false ;
}
char lineBuf [ 256 ] ;
int section = - 1 ; // 0: style, 1: behaviors, 2: colors
for ( char * line = fgets ( lineBuf , sizeof ( lineBuf ) , f ) ;
line ! = nullptr ;
line = fgets ( lineBuf , sizeof ( lineBuf ) , f ) )
{
// skip whitespace, if any
line = skipWhitespace ( line ) ;
if ( * line = = ' # ' ) // skip comment lines
continue ;
if ( * line = = ' [ ' ) { // start of a section
const char * secStr = line + 1 ; // skip '['
secStr = skipWhitespace ( secStr ) ;
// "[style]" "[behaviors]" "[colors]"
if ( idStr : : Icmpn ( secStr , " style " , 5 ) = = 0 ) {
section = 0 ;
} else if ( idStr : : Icmpn ( secStr , " behaviors " , 9 ) = = 0 ) {
section = 1 ;
} else if ( idStr : : Icmpn ( secStr , " colors " , 6 ) = = 0 ) {
section = 2 ;
} else {
common - > Warning ( " Invalid line that looks like a section in ImGui style: '%s' \n " , line ) ;
}
continue ;
}
if ( * line = = ' \r ' | | * line = = ' \n ' ) {
continue ; // empty line
}
if ( section = = 0 ) {
parseStyleLine ( s , line ) ;
} else if ( section = = 1 ) {
parseBehaviorLine ( s , line ) ;
} else if ( section = = 2 ) {
parseColorLine ( s , line ) ;
} else {
common - > Warning ( " Invalid line in ImGui before start of any section: '%s' \n " , line ) ;
}
}
return true ;
}
// check correctness of the X macros above (detect when something is added to ImGuiStyle)
namespace {
# define D3_IMSTYLE_COLOR(C) + 1
// "0 D3_IMSTYLE_COLORS" is expanded to 0 + 1 + 1 ... for each D3_IMSTYLE_COLOR entry
// => it should have the same value as the ImGuiCol_COUNT constant
static_assert ( ImGuiCol_COUNT = = 0 D3_IMSTYLE_COLORS ,
" something was added or removed in enum ImGuiCol_ => adjust D3_IMSTYLE_COLORS table above " ) ;
# undef D3_IMSTYLE_COLOR
// recreate struct ImGuiStyle from the tables above and see if they're identical
// (this struct is only used for the static assertions below)
struct D3_ImGuiStyle_Check {
# define D3_IMATTR_FLOAT( NAME ) float NAME ;
# define D3_IMATTR_VEC2( NAME ) ImVec2 NAME ;
# define D3_IMATTR_INT( NAME ) int NAME ;
# define D3_IMATTR_BOOL( NAME ) bool NAME ;
// this expands to all the ImGuiStyle members, up to (excluding) Colors
// exactly like in ImGuiStyle (except the pseudo-enums like ImGuiDir are plain ints here)
D3_IMSTYLE_ATTRS
ImVec4 Colors [ ImGuiCol_COUNT ] ;
// just like the other members/attributes, expand the behaviors
D3_IMSTYLE_BEHAVIORS
# undef D3_IMATTR_FLOAT
# undef D3_IMATTR_VEC2
# undef D3_IMATTR_INT
# undef D3_IMATTR_BOOL
} ;
template < class T , class U >
struct is_same {
static constexpr bool value = false ;
} ;
template < class T >
struct is_same < T , T > {
static constexpr bool value = true ;
} ;
# define D3_IMATTR_FLOAT( NAME ) \
static_assert ( offsetof ( ImGuiStyle , NAME ) = = offsetof ( D3_ImGuiStyle_Check , NAME ) , \
" member " # NAME " not at expected offset - is the member before it missing from the list, or moved to another position? " ) ; \
static_assert ( is_same < decltype ( ImGuiStyle : : NAME ) , float > : : value , " expected member " # NAME " to be a float - adjust the list! " ) ;
# define D3_IMATTR_VEC2( NAME ) \
static_assert ( offsetof ( ImGuiStyle , NAME ) = = offsetof ( D3_ImGuiStyle_Check , NAME ) , \
" member " # NAME " not at expected offset - is the member before it missing from the list, or moved to another position? " ) ; \
static_assert ( is_same < decltype ( ImGuiStyle : : NAME ) , ImVec2 > : : value , " expected member " # NAME " to be an ImVec2 - adjust the list! " ) ;
# define D3_IMATTR_INT( NAME ) \
static_assert ( offsetof ( ImGuiStyle , NAME ) = = offsetof ( D3_ImGuiStyle_Check , NAME ) , \
" member " # NAME " not at expected offset - is the member before it missing from the list, or moved to another position? " ) ; \
static_assert ( is_same < decltype ( ImGuiStyle : : NAME ) , int > : : value , " expected member " # NAME " to be an int - adjust the list! " ) ;
# define D3_IMATTR_BOOL( NAME ) \
static_assert ( offsetof ( ImGuiStyle , NAME ) = = offsetof ( D3_ImGuiStyle_Check , NAME ) , \
" member " # NAME " not at expected offset - is the member before it missing from the list, or moved to another position? " ) ; \
static_assert ( is_same < decltype ( ImGuiStyle : : NAME ) , bool > : : value , " expected member " # NAME " to be a bool - adjust the list! " ) ;
// expanding those static assertions for offset and type for all attributes and behaviors
D3_IMSTYLE_ATTRS
D3_IMSTYLE_BEHAVIORS
# undef D3_IMATTR_FLOAT
# undef D3_IMATTR_VEC2
# undef D3_IMATTR_INT
# undef D3_IMATTR_BOOL
static_assert ( offsetof ( ImGuiStyle , Colors ) = = offsetof ( D3_ImGuiStyle_Check , Colors ) , " member Colors not at expected offset " ) ;
// if all other static assertions passed and the following failed, probably a member was added at the end of the ImGuiStyle struct
static_assert ( sizeof ( D3_ImGuiStyle_Check ) = = sizeof ( ImGuiStyle ) ,
" something seems to be missing or wrong type in D3_IMSTYLE_ATTRS or D3_IMSTYLE_BEHAVIORS " ) ;
} //anon namespace
2024-03-27 00:02:03 +00:00
} } //namespace D3::ImGuiHooks