diff --git a/README.md b/README.md index c3f7767d6..3062cdb40 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Developed by [Omar Cornut](http://www.miracleworld.net). The library was develop Embeds [proggy_clean](http://www.proggyfonts.net/) font by Tristan Grimmer (also MIT license). -Inspiration, feedback, and testing: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Matt Willis. Thanks! +Inspiration, feedback, and testing: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Thanks! License ------- diff --git a/imconfig.h b/imconfig.h index 98b16f1b8..05a4c5bd0 100644 --- a/imconfig.h +++ b/imconfig.h @@ -39,3 +39,25 @@ namespace ImGui void Value(const char* prefix, const MyVec4& v, const char* float_format = NULL); }; */ + +//---- Define malloc/free/realloc functions to override internal memory allocations for ImGui +/* + * #define IM_MALLOC imgui_malloc + * #define IM_FREE imgui_free + * #define IM_REALLOC imgui_realloc + * + * void* imgui_malloc(size_t size); + * void imgui_free(void *ptr); + * void* imgui_realloc(void *ptr, size_t size); + */ +#ifndef IM_MALLOC + #define IM_MALLOC malloc +#endif + +#ifndef IM_FREE + #define IM_FREE free +#endif + +#ifndef IM_REALLOC + #define IM_REALLOC realloc +#endif diff --git a/imgui.cpp b/imgui.cpp index 4806d853d..faff067bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -109,7 +109,6 @@ ISSUES AND TODO-LIST - - misc: allow user to call NewFrame() multiple times without a render. - misc: merge ImVec4 / ImGuiAabb, they are essentially duplicate containers - window: autofit is losing its purpose when user relies on any dynamic layout (window width multiplier, column). maybe just discard autofit? - window: support horizontal scroll @@ -120,11 +119,11 @@ - main: make IsHovered() more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes - main: make IsHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? - scrollbar: use relative mouse movement when first-clicking inside of scroll grab box. + - scrollbar: make the grab visible and a minimum size for long scroll regions - input number: optional range min/max - input number: holding [-]/[+] buttons should increase the step non-linearly - input number: rename Input*() to Input(), Slider*() to Slider() ? - - layout: clean up the InputFloat3/SliderFloat3/ColorEdit4 horrible layout code. item width should include frame padding, then we can have a generic horizontal layout helper. - - add input4 helper (once above layout helpers are in they'll be smaller) + - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 horrible layout code. item width should include frame padding, then we can have a generic horizontal layout helper. - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) - columns: columns header to act as button (~sort op) and allow resize/reorder - columns: user specify columns size @@ -145,8 +144,10 @@ - filters: set a current filter that tree node can automatically query to hide themselves - filters: handle wildcards (with implicit leading/trailing *), regexps - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus) - - keyboard: full keyboard navigation and focus + - input: keyboard: full keyboard navigation and focus. + - input: support trackpad style scrolling & slider edit. - misc: not thread-safe + - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? - optimisation/render: use indexed rendering - optimisation/render: move clip-rect to vertex data? would allow merging all commands - optimisation/render: merge command-list of all windows into one command-list? @@ -199,7 +200,6 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs); }; // namespace ImGui static char* ImStrDup(const char *str); -static void ImStrDup_Free(char *str); //----------------------------------------------------------------------------- // Platform dependant default implementations @@ -270,9 +270,6 @@ ImGuiIO::ImGuiIO() { memset(this, 0, sizeof(*this)); - MallocFn = malloc; - FreeFn = free; - DeltaTime = 1.0f/60.0f; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; @@ -596,7 +593,7 @@ struct ImGuiIniData bool Collapsed; ImGuiIniData() { memset(this, 0, sizeof(*this)); } - ~ImGuiIniData() { if (Name) { ImStrDup_Free(Name); Name = NULL; } } + ~ImGuiIniData() { if (Name) { IM_FREE(Name); Name = NULL; } } }; struct ImGuiState @@ -635,7 +632,7 @@ struct ImGuiState // Logging bool LogEnabled; FILE* LogFile; - ImGuiTextBuffer LogClipboard; + ImGuiTextBuffer* LogClipboard; int LogAutoExpandMaxDepth; ImGuiState() @@ -658,6 +655,7 @@ struct ImGuiState LogEnabled = false; LogFile = NULL; LogAutoExpandMaxDepth = 2; + LogClipboard = NULL; } }; @@ -943,7 +941,7 @@ ImGuiWindow::ImGuiWindow(const char* name, ImVec2 default_pos, ImVec2 default_si FocusIdxRequestCurrent = IM_INT_MAX; FocusIdxRequestNext = IM_INT_MAX; - void *buff = GImGui.IO.MallocFn(sizeof(ImDrawList)); + void *buff = IM_MALLOC(sizeof(ImDrawList)); IM_ASSERT(buff); DrawList = new(buff) ImDrawList(); } @@ -951,9 +949,9 @@ ImGuiWindow::ImGuiWindow(const char* name, ImVec2 default_pos, ImVec2 default_si ImGuiWindow::~ImGuiWindow() { DrawList->~ImDrawList(); - GImGui.IO.FreeFn(DrawList); + IM_FREE(DrawList); DrawList = NULL; - ImStrDup_Free(Name); + IM_FREE(Name); Name = NULL; } @@ -1013,8 +1011,8 @@ void ImGuiWindow::AddToRenderList() for (size_t i = 0; i < DC.ChildWindows.size(); i++) { ImGuiWindow* child = DC.ChildWindows[i]; - IM_ASSERT(child->Visible); // Shouldn't be in this list if we are not active this frame - child->AddToRenderList(); + if (child->Visible) // clipped childs may have been marked not Visible + child->AddToRenderList(); } } @@ -1034,7 +1032,7 @@ static ImGuiIniData* FindWindowSettings(const char* name) return ini; } - void *buff = GImGui.IO.MallocFn(sizeof(ImGuiIniData)); + void *buff = IM_MALLOC(sizeof(ImGuiIniData)); ImGuiIniData* ini = new(buff) ImGuiIniData(); ini->Name = ImStrDup(name); ini->Collapsed = false; @@ -1064,12 +1062,12 @@ static void LoadSettings() return; if (fseek(f, 0, SEEK_SET)) return; - char* f_data = (char*)g.IO.MallocFn(f_size+1); + char* f_data = (char*)IM_MALLOC(f_size+1); f_size = (long)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) { - g.IO.FreeFn(f_data); + IM_FREE(f_data); return; } f_data[f_size] = 0; @@ -1103,7 +1101,7 @@ static void LoadSettings() line_start = line_end+1; } - g.IO.FreeFn(f_data); + IM_FREE(f_data); } static void SaveSettings() @@ -1177,7 +1175,9 @@ void NewFrame() // Initialize on first frame IM_ASSERT(g.Settings.empty()); - g.LogClipboard.init(); + void *lcbuff = IM_MALLOC(sizeof(ImGuiTextBuffer)); + IM_ASSERT(lcbuff); + g.LogClipboard = new(lcbuff) ImGuiTextBuffer(); LoadSettings(); if (!g.IO.Font) @@ -1186,7 +1186,7 @@ void NewFrame() const void* fnt_data; unsigned int fnt_size; ImGui::GetDefaultFontData(&fnt_data, &fnt_size, NULL, NULL); - void *buff = g.IO.MallocFn(sizeof(ImBitmapFont)); + void *buff = IM_MALLOC(sizeof(ImBitmapFont)); g.IO.Font = new(buff) ImBitmapFont(); g.IO.Font->LoadFromMemory(fnt_data, fnt_size); g.IO.FontHeight = g.IO.Font->GetFontSize(); @@ -1265,7 +1265,7 @@ void NewFrame() float scale = new_font_scale / window->FontScale; window->FontScale = new_font_scale; - ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; window->Pos += offset; window->PosFloat += offset; window->Size *= scale; @@ -1289,11 +1289,17 @@ void NewFrame() // Mark all windows as not visible for (size_t i = 0; i != g.Windows.size(); i++) - g.Windows[i]->Visible = false; + { + ImGuiWindow* window = g.Windows[i]; + window->Visible = false; + window->Accessed = false; + } - // Create implicit window - // We will only render it if the user has added something to it. - IM_ASSERT(g.CurrentWindowStack.empty()); // No window should be open at the beginning of the frame! + // No window should be open at the beginning of the frame. + // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. + g.CurrentWindowStack.clear(); + + // Create implicit window - we will only render it if the user has added something to it. ImGui::Begin("Debug", NULL, ImVec2(400,400)); } @@ -1308,7 +1314,7 @@ void Shutdown() for (size_t i = 0; i < g.Windows.size(); i++) { g.Windows[i]->~ImGuiWindow(); - g.IO.FreeFn(g.Windows[i]); + IM_FREE(g.Windows[i]); } g.Windows.clear(); g.CurrentWindowStack.clear(); @@ -1317,7 +1323,7 @@ void Shutdown() g.HoveredWindowExcludingChilds = NULL; for (size_t i = 0; i < g.Settings.size(); i++) { g.Settings[i]->~ImGuiIniData(); - g.IO.FreeFn(g.Settings[i]); + IM_FREE(g.Settings[i]); } g.Settings.clear(); g.RenderDrawLists.clear(); @@ -1330,17 +1336,21 @@ void Shutdown() if (g.IO.Font) { g.IO.Font->~ImBitmapFont(); - g.IO.FreeFn(g.IO.Font); + IM_FREE(g.IO.Font); g.IO.Font = NULL; } if (g.PrivateClipboard) { - GImGui.IO.FreeFn(g.PrivateClipboard); + IM_FREE(g.PrivateClipboard); g.PrivateClipboard = NULL; } - g.LogClipboard.destroy(); + if (g.LogClipboard) + { + g.LogClipboard->~ImGuiTextBuffer(); + IM_FREE(g.LogClipboard); + } g.Initialized = false; } @@ -1353,7 +1363,8 @@ static void AddWindowToSortedBuffer(ImGuiWindow* window, ImVector& for (size_t i = 0; i < window->DC.ChildWindows.size(); i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - AddWindowToSortedBuffer(child, sorted_windows); + if (child->Visible) + AddWindowToSortedBuffer(child, sorted_windows); } } } @@ -1500,9 +1511,9 @@ static void LogText(const ImVec2& ref_pos, const char* text, const char* text_en else { if (log_new_line || !is_first_line) - g.LogClipboard.append("\n%*s%.*s", tree_depth*4, "", char_count, text_remaining); + g.LogClipboard->append("\n%*s%.*s", tree_depth*4, "", char_count, text_remaining); else - g.LogClipboard.append(" %.*s", char_count, text_remaining); + g.LogClipboard->append(" %.*s", char_count, text_remaining); } } @@ -1820,9 +1831,10 @@ bool Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, ImGuiWin ImGuiWindow* window = FindWindow(name); if (!window) { - void *buff = g.IO.MallocFn(sizeof(ImGuiWindow)); + void *buff = IM_MALLOC(sizeof(ImGuiWindow)); IM_ASSERT(buff); + // Create window the first time, and load settings if (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) { window = new(buff) ImGuiWindow(name, ImVec2(0,0), size); @@ -2065,7 +2077,8 @@ bool Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, ImGuiWin ImGuiAabb scrollbar_bb(window->Aabb().Max.x - style.ScrollBarWidth, title_bar_aabb.Max.y+1, window->Aabb().Max.x, window->Aabb().Max.y-1); //window->DrawList->AddLine(scrollbar_bb.GetTL(), scrollbar_bb.GetBL(), g.Colors[ImGuiCol_Border]); window->DrawList->AddRectFilled(scrollbar_bb.Min, scrollbar_bb.Max, window->Color(ImGuiCol_ScrollbarBg)); - scrollbar_bb.Expand(ImVec2(-3,-3)); + scrollbar_bb.Max.x -= 3; + scrollbar_bb.Expand(ImVec2(0,-3)); const float grab_size_y_norm = ImSaturate(window->Size.y / ImMax(window->SizeContentsFit.y, window->Size.y)); const float grab_size_y = scrollbar_bb.GetHeight() * grab_size_y_norm; @@ -2143,24 +2156,38 @@ bool Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, ImGuiWin if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) { RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true); - RenderText(window->Pos + style.FramePadding + ImVec2(window->FontSize() + style.ItemInnerSpacing.x, 0), name); if (open) ImGui::CloseWindowButton(open); + + const ImVec2 text_size = CalcTextSize(name); + const ImVec2 text_min = window->Pos + style.FramePadding + ImVec2(window->FontSize() + style.ItemInnerSpacing.x, 0.0f); + const ImVec2 text_max = window->Pos + ImVec2(window->Size.x - (open ? (title_bar_aabb.GetHeight()-3) : style.FramePadding.x), style.FramePadding.y + text_size.y); + const bool clip_title = text_size.x > (text_max.x - text_min.x); // only push a clip rectangle if we need to, because it may turn into a separate draw call + if (clip_title) + ImGui::PushClipRect(ImVec4(text_min.x, text_min.y, text_max.x, text_max.y)); + RenderText(text_min, name); + if (clip_title) + ImGui::PopClipRect(); } } else { // Outer clipping rectangle if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ComboBox)) - ImGui::PushClipRect(g.CurrentWindowStack[g.CurrentWindowStack.size()-2]->ClipRectStack.back()); + { + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.size()-2]; + ImGui::PushClipRect(parent_window->ClipRectStack.back()); + } else + { ImGui::PushClipRect(ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y)); + } } // Inner clipping rectangle // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame const ImGuiAabb title_bar_aabb = window->TitleBarAabb(); - ImVec4 clip_rect(title_bar_aabb.Min.x+0.5f, title_bar_aabb.Max.y+0.5f, window->Aabb().Max.x-1.5f, window->Aabb().Max.y-1.5f); + ImVec4 clip_rect(title_bar_aabb.Min.x+0.5f+window->WindowPadding().x*0.5f, title_bar_aabb.Max.y+0.5f, window->Aabb().Max.x+0.5f-window->WindowPadding().x*0.5f, window->Aabb().Max.y-1.5f); if (window->ScrollbarY) clip_rect.z -= g.Style.ScrollBarWidth; ImGui::PushClipRect(clip_rect); @@ -2171,6 +2198,21 @@ bool Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, ImGuiWin window->Accessed = false; } + // Child window can be out of sight and have "negative" clip windows. + // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). + if (flags & ImGuiWindowFlags_ChildWindow) + { + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); + const ImVec4 clip_rect = window->ClipRectStack.back(); + window->Collapsed = (clip_rect.x >= clip_rect.z || clip_rect.y >= clip_rect.w); + + // We also hide the window from rendering because we've already added its border to the command list. + // (we could perform the check earlier in the function but it is simplier at this point) + // FIXME-WIP + if (window->Collapsed) + window->Visible = false; + } + // Return collapsed so that user can perform an early out optimisation return !window->Collapsed; } @@ -2202,12 +2244,12 @@ void End() fclose(g.LogFile); g.LogFile = NULL; } - if (g.LogClipboard.size() > 1) + if (g.LogClipboard->size() > 1) { - g.LogClipboard.append("\n"); + g.LogClipboard->append("\n"); if (g.IO.SetClipboardTextFn) - g.IO.SetClipboardTextFn(g.LogClipboard.begin(), g.LogClipboard.end()); - g.LogClipboard.clear(); + g.IO.SetClipboardTextFn(g.LogClipboard->begin(), g.LogClipboard->end()); + g.LogClipboard->clear(); } } @@ -2721,7 +2763,6 @@ static bool CloseWindowButton(bool* open) ImGuiWindow* window = GetCurrentWindow(); const ImGuiID id = window->GetID("##CLOSE"); - const float title_bar_height = window->TitleBarHeight(); const ImGuiAabb bb(window->Aabb().GetTR() + ImVec2(-title_bar_height+3.0f,2.0f), window->Aabb().GetTR() + ImVec2(-2.0f,+title_bar_height-2.0f)); @@ -2731,7 +2772,6 @@ static bool CloseWindowButton(bool* open) // Render const ImU32 col = window->Color((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); window->DrawList->AddCircleFilled(bb.GetCenter(), ImMax(2.0f,title_bar_height*0.5f-4), col, 16); - //RenderFrame(bb.Min, bb.Max, col, false); const float cross_padding = 4; if (hovered && bb.GetWidth() >= (cross_padding+1)*2 && bb.GetHeight() >= (cross_padding+1)*2) @@ -3277,7 +3317,7 @@ bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* disp return changed; } -bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power) +static bool SliderFloatN(const char* label, float v[3], int components, float v_min, float v_max, const char* display_format, float power) { ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -3285,63 +3325,46 @@ bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const return false; const ImGuiStyle& style = g.Style; - - bool value_changed = false; - ImGui::PushID(label); - - const int components = 2; const float w_full = window->DC.ItemWidth.back(); const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x)*(components-1)) / (float)components)); const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x)*(components-1))); + bool value_changed = false; + ImGui::PushID(label); ImGui::PushItemWidth(w_item_one); - value_changed |= ImGui::SliderFloat("##X", &v[0], v_min, v_max, display_format, power); - ImGui::SameLine(0, 0); - ImGui::PopItemWidth(); - ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::SliderFloat("##Y", &v[1], v_min, v_max, display_format, power); - ImGui::SameLine(0, 0); + for (int i = 0; i < components; i++) + { + ImGui::PushID(i); + if (i + 1 == components) + { + ImGui::PopItemWidth(); + ImGui::PushItemWidth(w_item_last); + } + value_changed |= ImGui::SliderFloat("##v", &v[i], v_min, v_max, display_format, power); + ImGui::SameLine(0, 0); + ImGui::PopID(); + } ImGui::PopItemWidth(); + ImGui::PopID(); ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); - ImGui::PopID(); return value_changed; } -bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power) +bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power) { - ImGuiState& g = GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->Collapsed) - return false; - - const ImGuiStyle& style = g.Style; - - bool value_changed = false; - ImGui::PushID(label); - - const int components = 3; - const float w_full = window->DC.ItemWidth.back(); - const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x)*(components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x)*(components-1))); - - ImGui::PushItemWidth(w_item_one); - value_changed |= ImGui::SliderFloat("##X", &v[0], v_min, v_max, display_format, power); - ImGui::SameLine(0, 0); - value_changed |= ImGui::SliderFloat("##Y", &v[1], v_min, v_max, display_format, power); - ImGui::SameLine(0, 0); - ImGui::PopItemWidth(); - - ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::SliderFloat("##Z", &v[2], v_min, v_max, display_format, power); - ImGui::SameLine(0, 0); - ImGui::PopItemWidth(); + return SliderFloatN(label, v, 2, v_min, v_max, display_format, power); +} - ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); +bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 3, v_min, v_max, display_format, power); +} - ImGui::PopID(); - return value_changed; +bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 4, v_min, v_max, display_format, power); } // Enum for ImGui::Plot() @@ -3902,7 +3925,7 @@ bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlag { // Remove new-line from pasted buffer size_t clipboard_len = strlen(clipboard); - char* clipboard_filtered = (char*)GImGui.IO.MallocFn(clipboard_len+1); + char* clipboard_filtered = (char*)IM_MALLOC(clipboard_len+1); int clipboard_filtered_len = 0; for (int i = 0; clipboard[i]; i++) { @@ -3913,7 +3936,7 @@ bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlag } clipboard_filtered[clipboard_filtered_len] = 0; stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); - GImGui.IO.FreeFn(clipboard_filtered); + IM_FREE(clipboard_filtered); } } else if (g.IO.InputCharacters[0]) @@ -3996,7 +4019,7 @@ bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlag return value_changed; } -bool InputFloat2(const char* label, float v[2], int decimal_precision) +static bool InputFloatN(const char* label, float* v, int components, int decimal_precision) { ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -4004,63 +4027,46 @@ bool InputFloat2(const char* label, float v[2], int decimal_precision) return false; const ImGuiStyle& style = g.Style; - - bool value_changed = false; - ImGui::PushID(label); - - const int components = 2; const float w_full = window->DC.ItemWidth.back(); const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1)) / (float)components)); const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1))); + bool value_changed = false; + ImGui::PushID(label); ImGui::PushItemWidth(w_item_one); - value_changed |= ImGui::InputFloat("##X", &v[0], 0, 0, decimal_precision); - ImGui::SameLine(0, 0); - ImGui::PopItemWidth(); - ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::InputFloat("##Y", &v[1], 0, 0, decimal_precision); - ImGui::SameLine(0, 0); + for (int i = 0; i < components; i++) + { + ImGui::PushID(i); + if (i + 1 == components) + { + ImGui::PopItemWidth(); + ImGui::PushItemWidth(w_item_last); + } + value_changed |= ImGui::InputFloat("##v", &v[i], 0, 0, decimal_precision); + ImGui::SameLine(0, 0); + ImGui::PopID(); + } ImGui::PopItemWidth(); + ImGui::PopID(); ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); - ImGui::PopID(); return value_changed; } -bool InputFloat3(const char* label, float v[3], int decimal_precision) +bool InputFloat2(const char* label, float v[2], int decimal_precision) { - ImGuiState& g = GImGui; - ImGuiWindow* window = GetCurrentWindow(); - if (window->Collapsed) - return false; - - const ImGuiStyle& style = g.Style; - - bool value_changed = false; - ImGui::PushID(label); - - const int components = 3; - const float w_full = window->DC.ItemWidth.back(); - const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one+style.FramePadding.x*2.0f+style.ItemInnerSpacing.x) * (components-1))); - - ImGui::PushItemWidth(w_item_one); - value_changed |= ImGui::InputFloat("##X", &v[0], 0, 0, decimal_precision); - ImGui::SameLine(0, 0); - value_changed |= ImGui::InputFloat("##Y", &v[1], 0, 0, decimal_precision); - ImGui::SameLine(0, 0); - ImGui::PopItemWidth(); - - ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::InputFloat("##Z", &v[2], 0, 0, decimal_precision); - ImGui::SameLine(0, 0); - ImGui::PopItemWidth(); + return InputFloatN(label, v, 2, decimal_precision); +} - ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); +bool InputFloat3(const char* label, float v[3], int decimal_precision) +{ + return InputFloatN(label, v, 3, decimal_precision); +} - ImGui::PopID(); - return value_changed; +bool InputFloat4(const char* label, float v[4], int decimal_precision) +{ + return InputFloatN(label, v, 4, decimal_precision); } static bool Combo_ArrayGetter(void* data, int idx, const char** out_text) @@ -4516,7 +4522,7 @@ static bool ClipAdvance(const ImGuiAabb& bb) window->DC.LastItemHovered = false; return true; } - window->DC.LastItemHovered = ImGui::IsMouseHoveringBox(bb); // this is a sensible default but widgets are GImGui.IO.FreeFn to override it after calling ClipAdvance + window->DC.LastItemHovered = ImGui::IsMouseHoveringBox(bb); // this is a sensible default but widgets are free to override it after calling ClipAdvance return false; } @@ -4602,6 +4608,8 @@ void Columns(int columns_count, const char* id, bool border) { ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); + if (window->Collapsed) + return; if (window->DC.ColumnsCount != 1) { @@ -5035,7 +5043,7 @@ ImBitmapFont::ImBitmapFont() void ImBitmapFont::Clear() { if (Data && DataOwned) - GImGui.IO.FreeFn(Data); + IM_FREE(Data); Data = NULL; DataOwned = false; Info = NULL; @@ -5058,7 +5066,7 @@ bool ImBitmapFont::LoadFromFile(const char* filename) return false; if (fseek(f, 0, SEEK_SET)) return false; - if ((Data = (unsigned char*)GImGui.IO.MallocFn(DataSize)) == NULL) + if ((Data = (unsigned char*)IM_MALLOC(DataSize)) == NULL) { fclose(f); return false; @@ -5066,7 +5074,7 @@ bool ImBitmapFont::LoadFromFile(const char* filename) if ((int)fread(Data, 1, DataSize, f) != DataSize) { fclose(f); - GImGui.IO.FreeFn(Data); + IM_FREE(Data); return false; } fclose(f); @@ -5309,7 +5317,7 @@ static const char* GetClipboardTextFn_DefaultImpl() static char* buf_local = NULL; if (buf_local) { - GImGui.IO.FreeFn(buf_local); + IM_FREE(buf_local); buf_local = NULL; } if (!OpenClipboard(NULL)) @@ -5357,12 +5365,12 @@ static void SetClipboardTextFn_DefaultImpl(const char* text, const char* text_en { if (GImGui.PrivateClipboard) { - GImGui.IO.FreeFn(GImGui.PrivateClipboard); + IM_FREE(GImGui.PrivateClipboard); GImGui.PrivateClipboard = NULL; } if (!text_end) text_end = text + strlen(text); - GImGui.PrivateClipboard = (char*)GImGui.IO.MallocFn(text_end - text + 1); + GImGui.PrivateClipboard = (char*)IM_MALLOC(text_end - text + 1); memcpy(GImGui.PrivateClipboard, text, text_end - text); GImGui.PrivateClipboard[text_end - text] = 0; } @@ -5575,11 +5583,14 @@ void ShowTestWindow(bool* open) ImGui::InputInt("input int", &i0); ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); - //static float vec2b[3] = { 0.10f, 0.20f }; - //ImGui::InputFloat2("input float2", vec2b); + //static float vec2a[3] = { 0.10f, 0.20f }; + //ImGui::InputFloat2("input float2", vec2a); - static float vec3b[3] = { 0.10f, 0.20f, 0.30f }; - ImGui::InputFloat3("input float3", vec3b); + static float vec3a[3] = { 0.10f, 0.20f, 0.30f }; + ImGui::InputFloat3("input float3", vec3a); + + //static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + //ImGui::InputFloat4("input float4", vec4a); static int i1=0; static int i2=42; @@ -5597,11 +5608,14 @@ void ShowTestWindow(bool* open) static float angle = 0.0f; ImGui::SliderAngle("angle", &angle); - //static float vec2a[3] = { 0.10f, 0.20f }; - //ImGui::SliderFloat2("slider float2", vec2a, 0.0f, 1.0f); + //static float vec2b[3] = { 0.10f, 0.20f }; + //ImGui::SliderFloat2("slider float2", vec2b, 0.0f, 1.0f); - static float vec3a[3] = { 0.10f, 0.20f, 0.30f }; - ImGui::SliderFloat3("slider float3", vec3a, 0.0f, 1.0f); + static float vec3b[3] = { 0.10f, 0.20f, 0.30f }; + ImGui::SliderFloat3("slider float3", vec3b, 0.0f, 1.0f); + + //static float vec4b[4] = { 0.10f, 0.20f, 0.30f, 0.40f }; + //ImGui::SliderFloat4("slider float4", vec4b, 0.0f, 1.0f); static float col1[3] = { 1.0f,0.0f,0.2f }; static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; @@ -5831,9 +5845,8 @@ void ShowTestWindow(bool* open) if (ImGui::CollapsingHeader("Long text")) { - /*static*/ ImGuiTextBuffer log; + static ImGuiTextBuffer log; static int lines = 0; - log.init(); ImGui::Text("Printing unusually long amount of text."); ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); if (ImGui::Button("Clear")) { log.clear(); lines = 0; } @@ -5857,18 +5870,12 @@ void ShowTestWindow(bool* open) static char* ImStrDup(const char *str) { - char *buff = (char*)GImGui.IO.MallocFn(strlen(str) + 1); + char *buff = (char*)IM_MALLOC(strlen(str) + 1); IM_ASSERT(buff); strcpy(buff, str); return buff; } -static void ImStrDup_Free(char *str) -{ - IM_ASSERT(str); - GImGui.IO.FreeFn(str); -} - //----------------------------------------------------------------------------- // Font data // Bitmap exported from proggy_clean.fon (c) by Tristan Grimmer http://www.proggyfonts.net @@ -6078,13 +6085,3 @@ void GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const voi }; //----------------------------------------------------------------------------- - -void* ImGui_ProxyMalloc(size_t size) -{ - return GImGui.IO.MallocFn(size); -} - -void ImGui_ProxyFree(void *ptr) -{ - GImGui.IO.FreeFn(ptr); -} diff --git a/imgui.h b/imgui.h index 7ed141494..c4fe0ffae 100644 --- a/imgui.h +++ b/imgui.h @@ -25,11 +25,6 @@ struct ImGuiWindow; #define IM_ASSERT(_EXPR) assert(_EXPR) #endif -#ifndef IM_MALLOC -#define IM_MALLOC malloc -#endif - - typedef unsigned int ImU32; typedef ImU32 ImGuiID; typedef int ImGuiCol; // enum ImGuiCol_ @@ -39,9 +34,6 @@ typedef int ImGuiWindowFlags; // enum ImGuiWindowFlags_ typedef int ImGuiInputTextFlags; // enum ImGuiInputTextFlags_ typedef ImBitmapFont* ImFont; -typedef void* (*ImGui_MallocCallback)(size_t size); -typedef void (*ImGui_FreeCallback)(void *ptr); - struct ImVec2 { float x, y; @@ -76,57 +68,46 @@ template class ImVector { private: - size_t _size; - size_t _capacity; - T* _data; + size_t Size; + size_t Capacity; + T* Data; public: typedef T value_type; typedef value_type* iterator; typedef const value_type* const_iterator; - ImVector() { _size = _capacity = 0; _data = NULL; } - ~ImVector() { if (_data) ImGui_ProxyFree(_data); } + ImVector() { Size = Capacity = 0; Data = NULL; } + ~ImVector() { if (Data) IM_FREE(Data); } - inline bool empty() const { return _size == 0; } - inline size_t size() const { return _size; } - inline size_t capacity() const { return _capacity; } + inline bool empty() const { return Size == 0; } + inline size_t size() const { return Size; } + inline size_t capacity() const { return Capacity; } - inline value_type& at(size_t i) { IM_ASSERT(i < _size); return _data[i]; } - inline const value_type& at(size_t i) const { IM_ASSERT(i < _size); return _data[i]; } - inline value_type& operator[](size_t i) { IM_ASSERT(i < _size); return _data[i]; } - inline const value_type& operator[](size_t i) const { IM_ASSERT(i < _size); return _data[i]; } + inline value_type& at(size_t i) { IM_ASSERT(i < Size); return Data[i]; } + inline const value_type& at(size_t i) const { IM_ASSERT(i < Size); return Data[i]; } + inline value_type& operator[](size_t i) { IM_ASSERT(i < Size); return Data[i]; } + inline const value_type& operator[](size_t i) const { IM_ASSERT(i < Size); return Data[i]; } - inline void clear() { if (_data) { _size = _capacity = 0; ImGui_ProxyFree(_data); _data = NULL; } } - inline iterator begin() { return _data; } - inline const_iterator begin() const { return _data; } - inline iterator end() { return _data + _size; } - inline const_iterator end() const { return _data + _size; } + inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } + inline iterator begin() { return Data; } + inline const_iterator begin() const { return Data; } + inline iterator end() { return Data + Size; } + inline const_iterator end() const { return Data + Size; } inline value_type& front() { return at(0); } inline const value_type& front() const { return at(0); } - inline value_type& back() { IM_ASSERT(_size > 0); return at(_size-1); } - inline const value_type& back() const { IM_ASSERT(_size > 0); return at(_size-1); } - inline void swap(ImVector& rhs) { const size_t rhs_size = rhs._size; rhs._size = _size; _size = rhs_size; const size_t rhs_cap = rhs._capacity; rhs._capacity = _capacity; _capacity = rhs_cap; value_type* rhs_data = rhs._data; rhs._data = _data; _data = rhs_data; } - - inline void reserve(size_t new_capacity) - { - if (!_data) { - _data = (value_type*)ImGui_ProxyMalloc(new_capacity*sizeof(value_type)); - } else { - void *tmp = ImGui_ProxyMalloc(new_capacity*sizeof(value_type)); - memcpy(tmp, _data, sizeof(value_type)*_capacity); - ImGui_ProxyFree(_data); - _data = (value_type*)tmp; - } - _capacity = new_capacity; - } - inline void resize(size_t new_size) { if (new_size > _capacity) reserve(new_size); _size = new_size; } - - inline void push_back(const value_type& v) { if (_size == _capacity) reserve(_capacity ? _capacity * 2 : 4); _data[_size++] = v; } - inline void pop_back() { IM_ASSERT(_size > 0); _size--; } - - inline iterator erase(const_iterator it) { IM_ASSERT(it >= begin() && it < end()); const int off = it - begin(); memmove(_data + off, _data + off + 1, (_size - off - 1) * sizeof(value_type)); _size--; return _data + off; } - inline void insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= begin() && it <= end()); const int off = it - begin(); if (_size == _capacity) reserve(_capacity ? _capacity * 2 : 4); if (off < (int)_size) memmove(_data + off + 1, _data + off, (_size - off) * sizeof(value_type)); _data[off] = v; _size++; } + inline value_type& back() { IM_ASSERT(Size > 0); return at(Size-1); } + inline const value_type& back() const { IM_ASSERT(Size > 0); return at(Size-1); } + inline void swap(ImVector& rhs) { const size_t rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; const size_t rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } + + inline void reserve(size_t new_capacity) { Data = (value_type*)IM_REALLOC(Data, new_capacity * sizeof(value_type)); Capacity = new_capacity; } + inline void resize(size_t new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; } + + inline void push_back(const value_type& v) { if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); Data[Size++] = v; } + inline void pop_back() { IM_ASSERT(Size > 0); Size--; } + + inline iterator erase(const_iterator it) { IM_ASSERT(it >= begin() && it < end()); const int off = it - begin(); memmove(Data + off, Data + off + 1, (Size - off - 1) * sizeof(value_type)); Size--; return Data + off; } + inline void insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= begin() && it <= end()); const int off = it - begin(); if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, (Size - off) * sizeof(value_type)); Data[off] = v; Size++; } }; #endif // #ifndef ImVector @@ -216,6 +197,7 @@ namespace ImGui bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + bool SliderFloat4(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); bool SliderAngle(const char* label, float* v, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); // *v in radians bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float)); @@ -227,6 +209,7 @@ namespace ImGui bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1); bool InputFloat2(const char* label, float v[2], int decimal_precision = -1); bool InputFloat3(const char* label, float v[3], int decimal_precision = -1); + bool InputFloat4(const char* label, float v[4], int decimal_precision = -1); bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100); bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0); bool Combo(const char* label, int* current_item, const char** items, int items_count, int popup_height_items = 7); @@ -401,8 +384,6 @@ struct ImGuiStyle struct ImGuiIO { // Settings (fill once) // Default value: - ImGui_MallocCallback MallocFn; - ImGui_FreeCallback FreeFn; ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Maximum time between saving .ini file, in seconds. Set to a negative value to disable .ini saving. @@ -508,15 +489,13 @@ struct ImGuiTextBuffer { ImVector Buf; - ImGuiTextBuffer() { } - void init() { if (Buf.empty()) Buf.push_back(0); } + ImGuiTextBuffer() { Buf.push_back(0); } const char* begin() const { return Buf.begin(); } const char* end() const { return Buf.end()-1; } size_t size() const { return Buf.size()-1; } bool empty() { return Buf.empty(); } void clear() { Buf.clear(); Buf.push_back(0); } void append(const char* fmt, ...); - void destroy() { Buf.clear(); } }; // Helper: Key->value storage