/* * Functions to read/write a Dear ImGui style (ImGuiStyle) from/to a (ini-like) * textfile, or generating C++ code for it. * * Hosted at https://github.com/DanielGibson/Snippets/ * * Written for/tested with Dear ImGui v1.90.6 * * If anything changes in struct ImGuiStyle or enum ImGuiCol_, this code should detect it * during compilation and give (hopefully) helpful errors with static_assert() (always check and * fix the first error that occurs first, the remaining ones might be caused by the first). * * Released under the same license as Dear ImGui: * * The MIT License (MIT) * * Copyright (c) 2024 Daniel Gibson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "imgui.h" #include #if 0 // this is basically the header, just copy it to wherever you want to use these functions namespace DG { // writes the given ImGuiStyle to the given filename (opened with fopen()) // returns true on success, false if opening the file failed extern bool WriteImGuiStyle( const ImGuiStyle& style, const char* filename ); // reads the the given filename (opened with fopen()) // and sets the given ImGuiStyle accordingly. // if any attributes/colors/behaviors are missing the the file, // they are not modified in style, so it probably makes sense to initialize // style to a sane default before calling this function. // returns true on success, false if opening the file failed extern bool ReadImGuiStyle( ImGuiStyle& style, const char* filename ); // generate C++ code that replicates the given style into a text buffer // (that you can write to a file or set the clipboard from or whatever) // if refStyle is set, only differences in style compared to refStyle are written extern ImGuiTextBuffer WriteImGuiStyleToCode( const ImGuiStyle& style, const ImGuiStyle* refStyle = nullptr ); } //namespace #endif // if you want to use another namespace than DG, modify this #define // (either here or through a compiler argumenet like -DDG_IMSAVESTYLE_NAMESPACE=horst) #ifndef DG_IMSAVESTYLE_NAMESPACE #define DG_IMSAVESTYLE_NAMESPACE DG #endif // #define your own DG_IMSAVESTYLE_FOPEN if you want to use an alternative to // standard fopen(), for example one that supports UTF-8 filenames on Windows #ifndef DG_IMSAVESTYLE_FOPEN #define DG_IMSAVESTYLE_FOPEN fopen #endif #undef warnPrintf // TODO: maybe use your own logging system instead of fprintf() to stderr //#define warnPrintf(...) fprintf( stderr, "Warning: " __VA_ARGS__ ) // DG: adjustment for dhewm3: #include "framework/Common.h" #undef strncmp // No, I don't want to use idStr::Cmpn() in this file. #define warnPrintf(...) common->Warning( __VA_ARGS__ ) // 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, // and have more then one kind of entry per table) // If you're wondering about the D3_ prefix on the macros, I originally wrote this for dhewm3. // this table contains all members of struct ImGuiStyle that come before the Colors array // when updating Dear ImGui, this might have to be adjusted for members that have been added/removed/renamed #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_DIR( 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_DIR( 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 ) // this table contains the members of struct ImGuiStyle that come after the Colors array // when updating Dear ImGui, this might have to be adjusted for members that have been added/removed/renamed #define D3_IMSTYLE_BEHAVIORS \ D3_IMATTR_FLOAT( HoverStationaryDelay ) \ D3_IMATTR_FLOAT( HoverDelayShort ) \ D3_IMATTR_FLOAT( HoverDelayNormal ) \ D3_IMATTR_INT( HoverFlagsForTooltipMouse ) \ D3_IMATTR_INT( HoverFlagsForTooltipNav ) // this table contains one entry for every value of enum ImGuiCol_ (except for ImGuiCol_COUNT) // as you can see, the "ImGuiCol_" prefix is left out in this table, // it's added programatically when needed // when updating Dear ImGui, this might have to be adjusted for enum members that have been added/removed/renamed #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 ) 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_DIR( NAME ) \ if ( sscanf( line, #NAME " = %d", &i ) == 1 ) { \ s . NAME = (ImGuiDir)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; // with the D3_IMATTR_* #defines above, the following // `D3_IMSTYLE_ATTRS` line turns into: // if ( sscanf( line, "Alpha = %f", &f1 ) == 1 ) { // s . Alpha = f1; // return; // } // if ( sscanf( line, "DisabledAlpha = %f", &f1 ) == 1 ) { // s . DisabledAlpha = f1; // return; // } // if ( sscanf( line, "WindowPadding = %f , %f", &f1, &f2 ) == 2 ) { // s . WindowPadding .x = f1; s . WindowPadding .y = f2; // return; // } // etc D3_IMSTYLE_ATTRS // NOTE: if a member is renamed, for backwards-compatibility you could add code here like // if ( sscanf( line, "OldName = %f", %f1 ) == 1 ) { // s.NewName = f; // return; // } warnPrintf( "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 // NOTE: same thing for backwards-compatbility as in parseStyleLine applies here warnPrintf( "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_DIR #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 // NOTE: here backwards-compat is also possible, like // if ( sscanf( line, "OldColorName = %f , %f , %f , %f", &c.x, &c.y, &c.z, &c.w) == 4 ) { // s.Colors[ ImGuiCol_NewColorName = c; // return; // } warnPrintf( "Invalid line in ImGui style under [colors] section: '%s'\n", line ); } namespace DG_IMSAVESTYLE_NAMESPACE { bool ReadImGuiStyle( ImGuiStyle& s, const char* filename ) { FILE* f = DG_IMSAVESTYLE_FOPEN( filename, "r" ); // TODO: "rt" on Windows? if ( f == nullptr ) { // DG: shut up this warning, user don't *have* to write their own style //warnPrintf( "Couldn't open '%s' for reading\n", filename ); 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 ( strncmp(secStr, "style", 5) == 0 ) { section = 0; } else if ( strncmp(secStr, "behaviors", 9) == 0 ) { section = 1; } else if ( strncmp(secStr, "colors", 6) == 0 ) { section = 2; } else { warnPrintf( "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 { warnPrintf( "Invalid line in ImGui before start of any section: '%s'\n", line ); } } return true; } bool WriteImGuiStyle( const ImGuiStyle& s, const char* filename ) { FILE* f = DG_IMSAVESTYLE_FOPEN( filename, "w" ); // TODO: "wt" on Windows? if ( f == nullptr ) { warnPrintf( "Couldn't open '%s' for writing\n", filename ); return false; } fprintf( f, "[style]\n" ); #define D3_IMATTR_FLOAT( NAME ) \ fprintf( f, "%s = %g\n", #NAME, s . NAME ); #define D3_IMATTR_VEC2( NAME ) \ fprintf( f, "%s = %g, %g\n", #NAME, s . NAME . x, s . NAME . y ); #define D3_IMATTR_INT( NAME ) \ fprintf( f, "%s = %d\n", #NAME, s . NAME ); #define D3_IMATTR_DIR( NAME ) \ fprintf( f, "%s = %d\n", #NAME, s . NAME ); #define D3_IMATTR_BOOL( NAME ) \ fprintf( f, "%s = %d\n", #NAME, (int)( s . NAME ) ); // this (together with the D3_IMATTR_* defines in the previous lines) // expands the D3_IMSTYLE_ATTRS table to // fprintf( f, "Alpha = %g\n", s . Alpha ); // fprintf( f, "DisabledAlpha = %g\n", s . DisabledAlpha ); // fprintf( f, "WindowPadding = %g %g\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_DIR #undef D3_IMATTR_BOOL fprintf( f, "\n[colors]\n" ); #define D3_IMSTYLE_COLOR( NAME ) { \ const ImVec4& c = s.Colors[ ImGuiCol_ ## NAME ]; \ fprintf( f, "%s = %g, %g, %g, %g\n", #NAME, c.x, c.y, c.z, c.w ); \ } // this turns into // { const ImVec4& c = s.Colors[ ImGuiCol_Text ]; fprintf( f, "Text = %g %g %g %g\n", c.x, c.y, c.z, c.w ); } // { const ImVec4& c = s.Colors[ ImGuiCol_TextDisabled ]; fprintf( f, "TextDisabled = %g %g %g %g\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 int numSpaces( const char* name, int targetLen ) { int ret = targetLen - strlen(name); return ret > 0 ? ret : 0; } ImGuiTextBuffer WriteImGuiStyleToCode( const ImGuiStyle& s, const ImGuiStyle* refStyle ) { ImGuiTextBuffer ret; ret.reserve(6000); ret.append( "ImGuiStyle& style = ImGui::GetStyle();\n" ); #define D3_IMATTR_FLOAT( NAME ) \ if ( refStyle == nullptr || s. NAME != refStyle-> NAME ) { \ ret.appendf( "style.%s %*s= %g;\n", #NAME, numSpaces( #NAME , 27 ), "", s. NAME ); \ } #define D3_IMATTR_VEC2( NAME ) \ if ( refStyle == nullptr || s. NAME .x != refStyle-> NAME .x || s. NAME .y != refStyle-> NAME .y ) { \ ret.appendf( "style.%s %*s= ImVec2( %g, %g );\n", #NAME, numSpaces( #NAME , 27 ), "", s. NAME .x, s. NAME .y ); \ } #define D3_IMATTR_INT( NAME ) \ if ( refStyle == nullptr || s. NAME != refStyle-> NAME ) { \ ret.appendf( "style.%s = %d; // TODO: flag\n", #NAME, s. NAME ); \ } #define D3_IMATTR_DIR( NAME ) \ if ( refStyle == nullptr || s. NAME != refStyle-> NAME ) { \ ret.appendf( "style.%s = %d;\n", #NAME, s. NAME ); \ } #define D3_IMATTR_BOOL( NAME ) \ if ( refStyle == nullptr || s. NAME != refStyle-> NAME ) { \ ret.appendf( "style.%s %*s= %s;\n", #NAME, numSpaces( #NAME , 27 ), "", s. NAME ? "true" : "false" ); \ } D3_IMSTYLE_ATTRS ret.append("\n"); D3_IMSTYLE_BEHAVIORS #undef D3_IMATTR_FLOAT #undef D3_IMATTR_VEC2 #undef D3_IMATTR_INT #undef D3_IMATTR_DIR #undef D3_IMATTR_BOOL ret.append( "\nImVec4* colors = style.Colors;\n" ); for ( ImGuiCol cIdx=0; cIdx < ImGuiCol_COUNT; ++cIdx ) { const ImVec4& c = s.Colors[cIdx]; if ( refStyle != nullptr ) { const ImVec4& c2 = refStyle->Colors[cIdx]; if ( c.x == c2.x && c.y == c2.y && c.z == c2.z && c.w == c2.w ) { // same colors, nothing to do continue; } } const char* name = ImGui::GetStyleColorName( cIdx ); ret.appendf( "colors[ ImGuiCol_%s ]%*s = ImVec4( %g, %g, %g, %g );\n", name, numSpaces( name , 21 ), "", c.x, c.y, c.z, c.w ); } ret.append("\n"); return ret; } } //namespace DG // check correctness of the X macro tables above (detect when something is added/removed/renamed in struct ImGuiStyle or enum ImGuiCol_) 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_DIR( NAME ) ImGuiDir 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_DIR #undef D3_IMATTR_BOOL }; template struct is_same { static constexpr bool value = false; }; template struct is_same { 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_DIR( 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 ), ImGuiDir >::value, "expected member " #NAME " to be an ImGuiDir - 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