Skip to content

Instantly share code, notes, and snippets.

@Flix01
Last active June 15, 2023 08:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Flix01/2c6e06b4a4f93016c334 to your computer and use it in GitHub Desktop.
Save Flix01/2c6e06b4a4f93016c334 to your computer and use it in GitHub Desktop.
imguistyleserializer
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
// WHAT'S THIS
// Extension to the imgui library v.1.21 wip (https://github.com/ocornut/imgui)
// to support saving/loading imgui styles.
// USAGE:
/*
1) Compile this cpp file
2) In your main.cpp just add (without including any additional file: there's no header!):
extern bool ImGuiSaveStyle(const char* filename,const ImGuiStyle& style);
extern bool ImGuiLoadStyle(const char* filename,ImGuiStyle& style);
3) Use them together with ImGui::GetStyle() to save/load the current style.
ImGui::GetStyle() returns a reference of the current style that can be set/get.
Please note that other style options are not globally serializable because they are "window flags",
that must be set on a per-window basis (for example border,titlebar,scrollbar,resizable,movable,per-window alpha).
To edit and save a style, you can use the default ImGui example and append to the "Debug" window the following code:
ImGui::Text("\n");
ImGui::Text("Please modify the current style in:");
ImGui::Text("ImGui Test->Window Options->Style Editor");
static bool loadCurrentStyle = false;
static bool saveCurrentStyle = false;
static bool resetCurrentStyle = false;
loadCurrentStyle = ImGui::Button("Load Saved Style");
saveCurrentStyle = ImGui::Button("Save Current Style");
resetCurrentStyle = ImGui::Button("Reset Current Style");
if (loadCurrentStyle) {
if (!ImGuiLoadStyle("./myimgui.style",ImGui::GetStyle())) {
fprintf(stderr,"Warning: \"./myimgui.style\" not present.\n");
}
}
if (saveCurrentStyle) {
if (!ImGuiSaveStyle("./myimgui.style",ImGui::GetStyle())) {
fprintf(stderr,"Warning: \"./myimgui.style\" cannot be saved.\n");
}
}
if (resetCurrentStyle) ImGui::GetStyle() = ImGuiStyle();
*/
#include <imgui.h>
// From <imgui.cpp>:--------------------------------------------------------
#include <stdio.h> // vsnprintf
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR)))
static size_t ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int w = vsnprintf(buf, buf_size, fmt, args);
va_end(args);
buf[buf_size-1] = 0;
return (w == -1) ? buf_size : (size_t)w;
}
//---------------------------------------------------------------------------
#include <string.h>
bool ImGuiSaveStyle(const char* filename,const ImGuiStyle& style)
{
// Write .style file
FILE* f = fopen(filename, "wt");
if (!f) return false;
fprintf(f, "[Alpha]\n%1.3f\n", style.Alpha);
fprintf(f, "[WindowPadding]\n%1.3f %1.3f\n", style.WindowPadding.x,style.WindowPadding.y);
fprintf(f, "[WindowMinSize]\n%1.3f %1.3f\n", style.WindowMinSize.x,style.WindowMinSize.y);
fprintf(f, "[FramePadding]\n%1.3f %1.3f\n", style.FramePadding.x,style.FramePadding.y);
fprintf(f, "[FrameRounding]\n%1.3f\n", style.FrameRounding);
fprintf(f, "[ItemSpacing]\n%1.3f %1.3f\n", style.ItemSpacing.x,style.ItemSpacing.y);
fprintf(f, "[ItemInnerSpacing]\n%1.3f %1.3f\n", style.ItemInnerSpacing.x,style.ItemInnerSpacing.y);
fprintf(f, "[TouchExtraPadding]\n%1.3f %1.3f\n", style.TouchExtraPadding.x,style.TouchExtraPadding.y);
fprintf(f, "[AutoFitPadding]\n%1.3f %1.3f\n", style.AutoFitPadding.x,style.AutoFitPadding.y);
fprintf(f, "[WindowFillAlphaDefault]\n%1.3f\n", style.WindowFillAlphaDefault);
fprintf(f, "[WindowRounding]\n%1.3f\n", style.WindowRounding);
fprintf(f, "[TreeNodeSpacing]\n%1.3f\n", style.TreeNodeSpacing);
fprintf(f, "[ColumnsMinSpacing]\n%1.3f\n", style.ColumnsMinSpacing);
fprintf(f, "[ScrollBarWidth]\n%1.3f\n", style.ScrollBarWidth);
for (size_t i = 0; i != ImGuiCol_COUNT; i++)
{
const ImVec4& c = style.Colors[i];
fprintf(f, "[%s]\n", ImGui::GetStyleColName(i));//ImGuiColNames[i]);
fprintf(f, "%1.3f %1.3f %1.3f %1.3f\n",c.x,c.y,c.z,c.w);
}
fprintf(f,"\n");
fclose(f);
return true;
}
bool ImGuiLoadStyle(const char* filename,ImGuiStyle& style)
{
// Load .style file
if (!filename) return false;
// Load file into memory
FILE* f;
if ((f = fopen(filename, "rt")) == NULL) return false;
if (fseek(f, 0, SEEK_END)) {
fclose(f);
return false;
}
const long f_size_signed = ftell(f);
if (f_size_signed == -1) {
fclose(f);
return false;
}
size_t f_size = (size_t)f_size_signed;
if (fseek(f, 0, SEEK_SET)) {
fclose(f);
return false;
}
char* f_data = (char*)ImGui::MemAlloc(f_size+1);
f_size = fread(f_data, 1, f_size, f); // Text conversion alter read size so let's not be fussy about return value
fclose(f);
if (f_size == 0) {
ImGui::MemFree(f_data);
return false;
}
f_data[f_size] = 0;
// Parse file in memory
char name[128];name[0]='\0';
const char* buf_end = f_data + f_size;
for (const char* line_start = f_data; line_start < buf_end; )
{
const char* line_end = line_start;
while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
line_end++;
if (name[0]=='\0' && line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
{
ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", line_end-line_start-2, line_start+1);
//fprintf(stderr,"name: %s\n",name); // dbg
}
else if (name[0]!='\0')
{
float *pf[4]= {0,0,0,0};
int npf = 0;
// parsing 'name' here by filling the fields above
{
if (strcmp(name, "Alpha")==0) {npf=1;pf[0]=&style.Alpha;}
else if (strcmp(name, "WindowPadding")==0) {npf=2;pf[0]=&style.WindowPadding.x;pf[1]=&style.WindowPadding.y;}
else if (strcmp(name, "WindowMinSize")==0) {npf=2;pf[0]=&style.WindowMinSize.x;pf[1]=&style.WindowMinSize.y;}
else if (strcmp(name, "FramePadding")==0) {npf=2;pf[0]=&style.FramePadding.x;pf[1]=&style.FramePadding.y;}
else if (strcmp(name, "FrameRounding")==0) {npf=1;pf[0]=&style.FrameRounding;}
else if (strcmp(name, "ItemSpacing")==0) {npf=2;pf[0]=&style.ItemSpacing.x;pf[1]=&style.ItemSpacing.y;}
else if (strcmp(name, "ItemInnerSpacing")==0) {npf=2;pf[0]=&style.ItemInnerSpacing.x;pf[1]=&style.ItemInnerSpacing.y;}
else if (strcmp(name, "TouchExtraPadding")==0) {npf=2;pf[0]=&style.TouchExtraPadding.x;pf[1]=&style.TouchExtraPadding.y;}
else if (strcmp(name, "AutoFitPadding")==0) {npf=2;pf[0]=&style.AutoFitPadding.x;pf[1]=&style.AutoFitPadding.y;}
else if (strcmp(name, "WindowFillAlphaDefault")==0) {npf=1;pf[0]=&style.WindowFillAlphaDefault;}
else if (strcmp(name, "WindowRounding")==0) {npf=1;pf[0]=&style.WindowRounding;}
else if (strcmp(name, "TreeNodeSpacing")==0) {npf=1;pf[0]=&style.TreeNodeSpacing;}
else if (strcmp(name, "ColumnsMinSpacing")==0) {npf=1;pf[0]=&style.ColumnsMinSpacing;}
else if (strcmp(name, "ScrollBarWidth")==0) {npf=1;pf[0]=&style.ScrollBarWidth;}
// all the colors here
else {
for (int j=0;j<ImGuiCol_COUNT;j++) {
if (strcmp(name,ImGui::GetStyleColName(j))==0) {
npf = 4;
ImVec4& color = style.Colors[j];
pf[0]=&color.x;pf[1]=&color.y;pf[2]=&color.z;pf[3]=&color.w;
break;
}
}
}
}
//fprintf(stderr,"name: %s npf=%d\n",name,npf); // dbg
// parsing values here and filling pf[]
float x,y,z,w;
switch (npf) {
case 1:
if (sscanf(line_start, "%f", &x) == npf) {
*pf[0] = x;
}
else fprintf(stderr,"Warning in ImGui::LoadStyle(\"%s\"): skipped [%s] (parsing error).\n",filename,name);
break;
case 2:
if (sscanf(line_start, "%f %f", &x, &y) == npf) {
*pf[0] = x;*pf[1] = y;
}
else fprintf(stderr,"Warning in ImGui::LoadStyle(\"%s\"): skipped [%s] (parsing error).\n",filename,name);
break;
case 3:
if (sscanf(line_start, "%f %f %f", &x, &y, &z) == npf) {
*pf[0] = x;*pf[1] = y;*pf[2] = z;
}
else fprintf(stderr,"Warning in ImGui::LoadStyle(\"%s\"): skipped [%s] (parsing error).\n",filename,name);
break;
case 4:
if (sscanf(line_start, "%f %f %f %f", &x, &y, &z, &w) == npf) {
*pf[0] = x;*pf[1] = y;*pf[2] = z;*pf[3] = w;
}
else fprintf(stderr,"Warning in ImGui::LoadStyle(\"%s\"): skipped [%s] (parsing error).\n",filename,name);
break;
default:
fprintf(stderr,"Warning in ImGui::LoadStyle(\"%s\"): skipped [%s] (unknown field).\n",filename,name);
break;
}
/*
// Same reference code from <imgui.cpp> to help parsing
float x, y;
int i;
if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
settings->Pos = ImVec2(x, y);
else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
settings->Collapsed = (i != 0);
*/
//---------------------------------------------------------------------------------
name[0]='\0'; // mandatory
}
line_start = line_end+1;
}
// Release memory
ImGui::MemFree(f_data);
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment