Settings: basic refactor so that additional data structures can be loaded/saved. Parser/saver is still the minimum viable poor-man parsing.

This commit is contained in:
omar 2017-11-27 23:55:42 +01:00
parent d552cabd15
commit 7e2d0d734c
2 changed files with 135 additions and 67 deletions

184
imgui.cpp
View File

@ -647,7 +647,7 @@ static ImGuiSettingsWindow* FindWindowSettings(const char* name);
static ImGuiSettingsWindow* AddWindowSettings(const char* name); static ImGuiSettingsWindow* AddWindowSettings(const char* name);
static void LoadIniSettingsFromDisk(const char* ini_filename); static void LoadIniSettingsFromDisk(const char* ini_filename);
static void LoadIniSettingsFromMemory(const char* buf, const char* buf_end = NULL); static void LoadIniSettingsFromMemory(const char* buf);
static void SaveIniSettingsToDisk(const char* ini_filename); static void SaveIniSettingsToDisk(const char* ini_filename);
static void SaveIniSettingsToMemory(ImVector<char>& out_buf); static void SaveIniSettingsToMemory(ImVector<char>& out_buf);
static void MarkIniSettingsDirty(ImGuiWindow* window); static void MarkIniSettingsDirty(ImGuiWindow* window);
@ -924,8 +924,16 @@ void ImStrncpy(char* dst, const char* src, int count)
char* ImStrdup(const char *str) char* ImStrdup(const char *str)
{ {
size_t len = strlen(str) + 1; size_t len = strlen(str) + 1;
void* buff = ImGui::MemAlloc(len); void* buf = ImGui::MemAlloc(len);
return (char*)memcpy(buff, (const void*)str, len); return (char*)memcpy(buf, (const void*)str, len);
}
char* ImStrchrRange(const char* str, const char* str_end, char c)
{
for ( ; str < str_end; str++)
if (*str == c)
return (char*)str;
return NULL;
} }
int ImStrlenW(const ImWchar* str) int ImStrlenW(const ImWchar* str)
@ -2463,12 +2471,75 @@ void ImGui::NewFrame()
ImGui::Begin("Debug##Default"); ImGui::Begin("Debug##Default");
} }
static void* SettingsHandlerWindow_ReadOpenEntry(ImGuiContext&, const char* name)
{
ImGuiSettingsWindow* settings = FindWindowSettings(name);
if (!settings)
settings = AddWindowSettings(name);
return (void*)settings;
}
static void SettingsHandlerWindow_ReadLine(ImGuiContext&, void* entry, const char* line)
{
ImGuiSettingsWindow* settings = (ImGuiSettingsWindow*)entry;
float x, y;
int i;
if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
}
static void SettingsHandlerWindow_WriteAll(ImGuiContext& g, ImGuiTextBuffer* buf)
{
// Gather data from windows that were active during this session
for (int i = 0; i != g.Windows.Size; i++)
{
ImGuiWindow* window = g.Windows[i];
if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
continue;
ImGuiSettingsWindow* settings = FindWindowSettings(window->Name);
if (!settings) // This will only return NULL in the rare instance where the window was first created with ImGuiWindowFlags_NoSavedSettings then had the flag disabled later on. We don't bind settings in this case (bug #1000).
continue;
settings->Pos = window->Pos;
settings->Size = window->SizeFull;
settings->Collapsed = window->Collapsed;
}
// Write a buffer
// If a window wasn't opened in this session we preserve its settings
buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
for (int i = 0; i != g.SettingsWindows.Size; i++)
{
const ImGuiSettingsWindow* settings = &g.SettingsWindows[i];
if (settings->Pos.x == FLT_MAX)
continue;
const char* name = settings->Name;
if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
name = p;
buf->appendf("[Window][%s]\n", name);
buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
buf->appendf("Collapsed=%d\n", settings->Collapsed);
buf->appendf("\n");
}
}
void ImGui::Initialize() void ImGui::Initialize()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer)); g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer(); IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
// Add .ini handle for ImGuiWindow type
ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "Window";
ini_handler.TypeHash = ImHash("Window", 0, 0);
ini_handler.ReadOpenEntryFn = SettingsHandlerWindow_ReadOpenEntry;
ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
g.SettingsHandlers.push_back(ini_handler);
// Load .ini file
IM_ASSERT(g.SettingsWindows.empty()); IM_ASSERT(g.SettingsWindows.empty());
LoadIniSettingsFromDisk(g.IO.IniFilename); LoadIniSettingsFromDisk(g.IO.IniFilename);
g.Initialized = true; g.Initialized = true;
@ -2506,7 +2577,6 @@ void ImGui::Shutdown()
g.MovingWindow = NULL; g.MovingWindow = NULL;
for (int i = 0; i < g.SettingsWindows.Size; i++) for (int i = 0; i < g.SettingsWindows.Size; i++)
ImGui::MemFree(g.SettingsWindows[i].Name); ImGui::MemFree(g.SettingsWindows[i].Name);
g.SettingsWindows.clear();
g.ColorModifiers.clear(); g.ColorModifiers.clear();
g.StyleModifiers.clear(); g.StyleModifiers.clear();
g.FontStack.clear(); g.FontStack.clear();
@ -2522,6 +2592,9 @@ void ImGui::Shutdown()
g.InputTextState.InitialText.clear(); g.InputTextState.InitialText.clear();
g.InputTextState.TempTextBuffer.clear(); g.InputTextState.TempTextBuffer.clear();
g.SettingsWindows.clear();
g.SettingsHandlers.clear();
if (g.LogFile && g.LogFile != stdout) if (g.LogFile && g.LogFile != stdout)
{ {
fclose(g.LogFile); fclose(g.LogFile);
@ -2562,54 +2635,64 @@ static ImGuiSettingsWindow* AddWindowSettings(const char* name)
return settings; return settings;
} }
// Zero-tolerance, poor-man .ini parsing
// FIXME: Write something less rubbish
static void LoadIniSettingsFromDisk(const char* ini_filename) static void LoadIniSettingsFromDisk(const char* ini_filename)
{ {
if (!ini_filename) if (!ini_filename)
return; return;
int file_size; char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1);
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
if (!file_data) if (!file_data)
return; return;
LoadIniSettingsFromMemory(file_data, file_data + file_size); LoadIniSettingsFromMemory(file_data);
ImGui::MemFree(file_data); ImGui::MemFree(file_data);
} }
static void LoadIniSettingsFromMemory(const char* buf, const char* buf_end) // Zero-tolerance, no error reporting, cheap .ini parsing
static void LoadIniSettingsFromMemory(const char* buf_readonly)
{ {
// For convenience and to make the code simpler, we'll write zero terminators inside the buffer. So let's create a writable copy.
char* buf = ImStrdup(buf_readonly);
char* buf_end = buf + strlen(buf);
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (!buf_end) void* entry_data = NULL;
buf_end = buf + strlen(buf); const ImGuiSettingsHandler* entry_handler = NULL;
ImGuiSettingsWindow* settings = NULL;
for (const char* line_start = buf; line_start < buf_end; ) char* line_end = NULL;
for (char* line = buf; line < buf_end; line = line_end + 1)
{ {
const char* line_end = line_start; // Skip new lines markers, then find end of the line
while (*line == '\n' || *line == '\r')
line++;
line_end = line;
while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
line_end++; line_end++;
line_end[0] = 0;
if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']') if (line[0] == '[' && line_end > line && line_end[-1] == ']')
{ {
char name[64]; // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1); char* name_end = line_end - 1;
settings = FindWindowSettings(name); *name_end = 0;
if (!settings) char* type_start = line + 1;
settings = AddWindowSettings(name); char* type_end = ImStrchrRange(type_start, name_end, ']');
} char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
else if (settings) if (type_start && type_end && name_start++ && name_end)
{ {
float x, y; const ImGuiID type_hash = ImHash(type_start, type_end - type_start, 0);
int i; entry_handler = NULL;
if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2) for (int handler_n = 0; handler_n < g.SettingsHandlers.Size && entry_handler == NULL; handler_n++)
settings->Pos = ImVec2(x, y); if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2) entry_handler = &g.SettingsHandlers[handler_n];
settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize); entry_data = entry_handler ? entry_handler->ReadOpenEntryFn(g, name_start) : NULL;
else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
settings->Collapsed = (i != 0);
} }
line_start = line_end+1;
} }
else if (entry_handler != NULL && entry_data != NULL)
{
// Let type handler parse the line
entry_handler->ReadLineFn(g, entry_data, line);
}
}
ImGui::MemFree(buf);
} }
static void SaveIniSettingsToDisk(const char* ini_filename) static void SaveIniSettingsToDisk(const char* ini_filename)
@ -2622,7 +2705,6 @@ static void SaveIniSettingsToDisk(const char* ini_filename)
ImVector<char> buf; ImVector<char> buf;
SaveIniSettingsToMemory(buf); SaveIniSettingsToMemory(buf);
// Write .ini file
FILE* f = ImFileOpen(ini_filename, "wt"); FILE* f = ImFileOpen(ini_filename, "wt");
if (!f) if (!f)
return; return;
@ -2635,39 +2717,11 @@ static void SaveIniSettingsToMemory(ImVector<char>& out_buf)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
g.SettingsDirtyTimer = 0.0f; g.SettingsDirtyTimer = 0.0f;
// Gather data from windows that were active during this session
for (int i = 0; i != g.Windows.Size; i++)
{
ImGuiWindow* window = g.Windows[i];
if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
continue;
ImGuiSettingsWindow* settings = FindWindowSettings(window->Name);
if (!settings) // This will only return NULL in the rare instance where the window was first created with ImGuiWindowFlags_NoSavedSettings then had the flag disabled later on. We don't bind settings in this case (bug #1000).
continue;
settings->Pos = window->Pos;
settings->Size = window->SizeFull;
settings->Collapsed = window->Collapsed;
}
// Write a buffer
// If a window wasn't opened in this session we preserve its settings
ImGuiTextBuffer buf; ImGuiTextBuffer buf;
buf.reserve(g.SettingsWindows.Size * 64); // ballpark reserve for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
for (int i = 0; i != g.SettingsWindows.Size; i++) g.SettingsHandlers[handler_n].WriteAllFn(g, &buf);
{
const ImGuiSettingsWindow* settings = &g.SettingsWindows[i];
if (settings->Pos.x == FLT_MAX)
continue;
const char* name = settings->Name;
if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
name = p;
buf.appendf("[%s]\n", name);
buf.appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
buf.appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
buf.appendf("Collapsed=%d\n", settings->Collapsed);
buf.appendf("\n");
}
buf.Buf.pop_back(); // Remove extra zero-terminator used by ImGuiTextBuffer
out_buf.swap(buf.Buf); out_buf.swap(buf.Buf);
} }

View File

@ -106,6 +106,7 @@ IMGUI_API int ImStricmp(const char* str1, const char* str2);
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count); IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count);
IMGUI_API void ImStrncpy(char* dst, const char* src, int count); IMGUI_API void ImStrncpy(char* dst, const char* src, int count);
IMGUI_API char* ImStrdup(const char* str); IMGUI_API char* ImStrdup(const char* str);
IMGUI_API char* ImStrchrRange(const char* str_begin, const char* str_end, char c);
IMGUI_API int ImStrlenW(const ImWchar* str); IMGUI_API int ImStrlenW(const ImWchar* str);
IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
@ -379,6 +380,15 @@ struct ImGuiSettingsWindow
bool Collapsed; bool Collapsed;
}; };
struct ImGuiSettingsHandler
{
const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']'
ImGuiID TypeHash; // == ImHash(TypeName, 0, 0)
void* (*ReadOpenEntryFn)(ImGuiContext& ctx, const char* name);
void (*ReadLineFn)(ImGuiContext& ctx, void* entry, const char* line);
void (*WriteAllFn)(ImGuiContext& ctx, ImGuiTextBuffer* out_buf);
};
// Mouse cursor data (used when io.MouseDrawCursor is set) // Mouse cursor data (used when io.MouseDrawCursor is set)
struct ImGuiMouseCursorData struct ImGuiMouseCursorData
{ {
@ -438,8 +448,6 @@ struct ImGuiContext
ImGuiWindow* ActiveIdWindow; ImGuiWindow* ActiveIdWindow;
ImGuiWindow* MovingWindow; // Track the child window we clicked on to move a window. ImGuiWindow* MovingWindow; // Track the child window we clicked on to move a window.
ImGuiID MovingWindowMoveId; // == MovingWindow->MoveId ImGuiID MovingWindowMoveId; // == MovingWindow->MoveId
ImVector<ImGuiSettingsWindow> SettingsWindows; // .ini Settings
float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero
ImVector<ImGuiColMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector<ImGuiColMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont()
@ -488,6 +496,11 @@ struct ImGuiContext
ImVector<char> PrivateClipboard; // If no custom clipboard handler is defined ImVector<char> PrivateClipboard; // If no custom clipboard handler is defined
ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor
// Settings
float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero
ImVector<ImGuiSettingsWindow> SettingsWindows; // .ini settings for ImGuiWindow
ImVector<ImGuiSettingsHandler> SettingsHandlers; // List of .ini settings handlers
// Logging // Logging
bool LogEnabled; bool LogEnabled;
FILE* LogFile; // If != NULL log to stdout/ file FILE* LogFile; // If != NULL log to stdout/ file
@ -532,7 +545,6 @@ struct ImGuiContext
ActiveIdWindow = NULL; ActiveIdWindow = NULL;
MovingWindow = NULL; MovingWindow = NULL;
MovingWindowMoveId = 0; MovingWindowMoveId = 0;
SettingsDirtyTimer = 0.0f;
SetNextWindowPosVal = ImVec2(0.0f, 0.0f); SetNextWindowPosVal = ImVec2(0.0f, 0.0f);
SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); SetNextWindowSizeVal = ImVec2(0.0f, 0.0f);
@ -565,6 +577,8 @@ struct ImGuiContext
MouseCursor = ImGuiMouseCursor_Arrow; MouseCursor = ImGuiMouseCursor_Arrow;
memset(MouseCursorData, 0, sizeof(MouseCursorData)); memset(MouseCursorData, 0, sizeof(MouseCursorData));
SettingsDirtyTimer = 0.0f;
LogEnabled = false; LogEnabled = false;
LogFile = NULL; LogFile = NULL;
LogClipboard = NULL; LogClipboard = NULL;