From 32dbe836d0cde14891d47ed054535283d0f567e2 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Sat, 29 Oct 2016 15:33:09 -0400 Subject: [PATCH 01/20] Add user-facing IsPopupOpen function --- imgui.cpp | 28 ++++++++++++++++++---------- imgui.h | 1 + 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 07466840b..9c1ea04a1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -687,7 +687,7 @@ static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_ static void CloseInactivePopups(); static void ClosePopupToLevel(int remaining); static void ClosePopup(ImGuiID id); -static bool IsPopupOpen(ImGuiID id); +static bool IsPopupIdOpen(ImGuiID id); static ImGuiWindow* GetFrontMostModalRootWindow(); static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid); @@ -3367,7 +3367,7 @@ void ImGui::EndTooltip() ImGui::End(); } -static bool IsPopupOpen(ImGuiID id) +static bool IsPopupIdOpen(ImGuiID id) { ImGuiContext& g = *GImGui; return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; @@ -3451,7 +3451,7 @@ static void ClosePopupToLevel(int remaining) static void ClosePopup(ImGuiID id) { - if (!IsPopupOpen(id)) + if (!IsPopupIdOpen(id)) return; ImGuiContext& g = *GImGui; ClosePopupToLevel(g.OpenPopupStack.Size - 1); @@ -3481,7 +3481,7 @@ static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = window->GetID(str_id); - if (!IsPopupOpen(id)) + if (!IsPopupIdOpen(id)) { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values return false; @@ -3515,12 +3515,20 @@ bool ImGui::BeginPopup(const char* str_id) return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders); } +bool ImGui::IsPopupOpen(const char* str_id) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(str_id); + return IsPopupIdOpen(id); +} + bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = window->GetID(name); - if (!IsPopupOpen(id)) + if (!IsPopupIdOpen(id)) { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values return false; @@ -8415,7 +8423,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f); const bool hovered = IsHovered(frame_bb, id); - bool popup_open = IsPopupOpen(id); + bool popup_open = IsPopupIdOpen(id); bool popup_opened_now = false; const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); @@ -8439,7 +8447,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi if (g.IO.MouseClicked[0]) { SetActiveID(0); - if (IsPopupOpen(id)) + if (IsPopupIdOpen(id)) { ClosePopup(id); } @@ -8453,7 +8461,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi } bool value_changed = false; - if (IsPopupOpen(id)) + if (IsPopupIdOpen(id)) { // Size default to hold ~7 items if (height_in_items < 0) @@ -8799,7 +8807,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) ImGuiWindow* backed_focused_window = g.FocusedWindow; bool pressed; - bool menu_is_open = IsPopupOpen(id); + bool menu_is_open = IsPopupIdOpen(id); bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus")); if (menuset_is_open) g.FocusedWindow = window; @@ -8865,7 +8873,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_open = true; if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' want_close = true; - if (want_close && IsPopupOpen(id)) + if (want_close && IsPopupIdOpen(id)) ClosePopupToLevel(GImGui->CurrentPopupStack.Size); if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) diff --git a/imgui.h b/imgui.h index edb1d68d5..c1a92ee0d 100644 --- a/imgui.h +++ b/imgui.h @@ -371,6 +371,7 @@ namespace ImGui // Popups IMGUI_API void OpenPopup(const char* str_id); // mark popup as open. popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). + IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (can't close them by clicking outside) IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp! From 718f00d6519a3b0f4bca209749c3a46d6d86451f Mon Sep 17 00:00:00 2001 From: James Wallis <31036241+jadwallis@users.noreply.github.com> Date: Tue, 15 Aug 2017 14:11:04 +0100 Subject: [PATCH 02/20] Make font atlas packing padding configurable --- imgui.h | 1 + imgui_draw.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 195cdccba..e5f8a6149 100644 --- a/imgui.h +++ b/imgui.h @@ -1382,6 +1382,7 @@ struct ImFontAtlas int TexWidth; // Texture width calculated during Build(). int TexHeight; // Texture height calculated during Build(). int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. + int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3eb8eea6f..acae0e208 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1061,6 +1061,7 @@ ImFontAtlas::ImFontAtlas() TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; TexWidth = TexHeight = TexDesiredWidth = 0; + TexGlyphPadding = 1; TexUvWhitePixel = ImVec2(0, 0); } @@ -1314,13 +1315,13 @@ bool ImFontAtlas::Build() } } - // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth if they wish. + // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height. TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512; TexHeight = 0; const int max_tex_height = 1024*32; stbtt_pack_context spc; - stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); + stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, TexGlyphPadding, NULL); // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). ImVector extra_rects; From 9239e91dc9133fa0fa681d0b67db2741de76f17f Mon Sep 17 00:00:00 2001 From: James Wallis <31036241+jadwallis@users.noreply.github.com> Date: Tue, 15 Aug 2017 14:12:32 +0100 Subject: [PATCH 03/20] Whitespace fix to previous --- imgui.h | 2 +- imgui_draw.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index e5f8a6149..1f13882f7 100644 --- a/imgui.h +++ b/imgui.h @@ -1382,7 +1382,7 @@ struct ImFontAtlas int TexWidth; // Texture width calculated during Build(). int TexHeight; // Texture height calculated during Build(). int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. - int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. + int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index acae0e208..6e94cfb6b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1061,7 +1061,7 @@ ImFontAtlas::ImFontAtlas() TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; TexWidth = TexHeight = TexDesiredWidth = 0; - TexGlyphPadding = 1; + TexGlyphPadding = 1; TexUvWhitePixel = ImVec2(0, 0); } From e682362f35c0ce859933fbbdd8e0b4e8fa5f2792 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 12:54:51 +0800 Subject: [PATCH 04/20] TODO list update, comments --- TODO.txt | 21 +++++++++++++++------ imgui_internal.h | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/TODO.txt b/TODO.txt index 67a9250eb..61d9e048d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -23,19 +23,22 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. - window: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? + - window: expose contents size. (#1045) !- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet. - scrolling/clipping: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?) + - drawlist: make it easier to toggle AA per primitive, so we can use e.g. non-AA fill + AA borders more naturally - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. - drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). - drawlist: avoid passing null (-9999,+9999) rectangle to end-user, instead perhaps pass rectangle based on io.DisplaySize? + - drawlist: primtiives/helpers to manipulate vertices post submission, so e.g. a quad/rect can be resized to fit later submitted content, _without_ using the ChannelSplit api - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. - main: find a way to preserve relative orders of multiple reappearing windows (so an app toggling between "modes" e.g. fullscreen vs all tools) won't lose relative ordering. - main: IsItemHovered() make it 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: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? - - main: rename the main "Debug" window to avoid ID collision with user who may want to use "Debug" with specific flags + - main: rename the main "Debug" window to avoid ID collision with user who may want to use "Debug" with specific flags. - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395) - widgets: clean up widgets internal toward exposing everything and stabilizing imgui_internals.h. @@ -64,6 +67,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - input number: use mouse wheel to step up/down - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack. + - layout: helper or a way to express ImGui::SameLine(ImGui::GetCursorStartPos().x + ImGui::CalcItemWidth() + ImGui::GetStyle().ItemInnerSpacing.x); in a simpler manner. + - layout: generalization of the above: a concept equivalent to word processor ruler tab stop ~ mini columns (position in X, no clipping implied) (vaguely relate to #267, #395, also what is used internally for menu items) - layout: horizontal layout helper (#97) - layout: horizontal flow until no space left (#404) - layout: more generic alignment state (left/right/centered) for single items? @@ -78,8 +83,9 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - columns: flag to add horizontal separator above/below? - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets) -!- color: the color helpers/types are a mess and needs sorting out. +!- color: the color conversion helpers/types are a mess and needs sorting out. - color: (api breaking) ImGui::ColorConvertXXX functions should be loose ImColorConvertXX to match imgui_internals.h + - coloredit: it is still somehow awkward to copy colors around (unless going through Hex mode). - plot: full featured plot/graph api w/ scrolling, zooming etc. all bell & whistle. why not! - plot: PlotLines() should use the polygon-stroke facilities, less verticles (currently issues with averaging normals) @@ -158,11 +164,12 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i !- settings: expose enough to save/load .ini from RAM instead of fopen - settings: write more decent code to allow saving/loading new fields - - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file + - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437) - stb: add defines to disable stb implementations !- style: better default styles. !- style: move border to style structure, remove _ShowBorder flag. + - style: border types: out-screen, in-screen, etc. - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. - style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier) - style: color-box not always square? @@ -171,6 +178,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - style: global scale setting. - style: WindowPadding needs to be EVEN as the 0.5 multiplier used on this value probably have a subtle effect on clip rectangle - style: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438, #707, #1223) + - style: gradients fill (#1223) ~ 2 bg colors for each fill? tricky with rounded shapes and using textures for corners. - style editor: color child window height expressed in multiple of line height. - log: LogButtons() options for specifying depth and/or hiding depth slider @@ -205,12 +213,13 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - keyboard: full keyboard navigation and focus. (#323) - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71) - - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style). - - input: support track pad style scrolling & slider edit. + - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) + - inputs: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style). + - inputs: support track pad style scrolling & slider edit. - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) - misc: provide HoveredTime and ActivatedTime to ease the creation of animations. + - misc: fix for compilation settings where stdcall isn't the default (e.g. vectorcall) (#1230) - remote: make a system like RemoteImGui first-class citizen/project (#75) - demo: demo: add a virtual scrolling example? diff --git a/imgui_internal.h b/imgui_internal.h index 5afb368e7..2803e3509 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -4,6 +4,8 @@ // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! // Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) // #define IMGUI_DEFINE_MATH_OPERATORS +// Define IM_PLACEMENT_NEW() macro helper. +// #define IMGUI_DEFINE_PLACEMENT_NEW #pragma once From a83f7083ed23aadbe8cb595cd53619b174ab0c5d Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 13:06:14 +0800 Subject: [PATCH 05/20] BeginPopupEx() uses ImGuiID internally --- imgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index acd69588a..b5518e937 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -614,7 +614,7 @@ static void MarkIniSettingsDirty(); static void PushColumnClipRect(int column_index = -1); static ImRect GetVisibleRect(); -static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags); +static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); static void CloseInactivePopups(); static void ClosePopupToLevel(int remaining); static void ClosePopup(ImGuiID id); @@ -3505,11 +3505,10 @@ static inline void ClearSetNextWindowData() g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false; } -static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) +static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = window->GetID(str_id); if (!IsPopupOpen(id)) { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values @@ -3536,12 +3535,13 @@ static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) bool ImGui::BeginPopup(const char* str_id) { - if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values return false; } - return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders); + return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders); } bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags) @@ -8534,7 +8534,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0); - if (BeginPopupEx(label, flags)) + if (BeginPopupEx(id, flags)) { // Display items Spacing(); @@ -8944,7 +8944,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { SetNextWindowPos(popup_pos, ImGuiCond_Always); ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); - menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) } return menu_is_open; From d2259f65e51df44c79cfc93a491ca0b871355fc2 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 13:42:41 +0800 Subject: [PATCH 06/20] Undo part of 32dbe836d0cde14891d47ed054535283d0f567e2 to keep the same name for both overloads (#891, #799) --- imgui.cpp | 33 +++++++++++++++------------------ imgui_internal.h | 1 + 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 404989239..958e07013 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -618,7 +618,6 @@ static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); static void CloseInactivePopups(); static void ClosePopupToLevel(int remaining); static void ClosePopup(ImGuiID id); -static bool IsPopupIdOpen(ImGuiID id); static ImGuiWindow* GetFrontMostModalRootWindow(); static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid); @@ -3396,12 +3395,6 @@ void ImGui::EndTooltip() ImGui::End(); } -static bool IsPopupIdOpen(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; -} - // Mark popup as open (toggle toward open state). // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). @@ -3480,7 +3473,7 @@ static void ClosePopupToLevel(int remaining) static void ClosePopup(ImGuiID id) { - if (!IsPopupIdOpen(id)) + if (!ImGui::IsPopupOpen(id)) return; ImGuiContext& g = *GImGui; ClosePopupToLevel(g.OpenPopupStack.Size - 1); @@ -3509,7 +3502,7 @@ static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (!IsPopupIdOpen(id)) + if (!ImGui::IsPopupOpen(id)) { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values return false; @@ -3544,12 +3537,16 @@ bool ImGui::BeginPopup(const char* str_id) return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders); } +bool ImGui::IsPopupOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; +} + bool ImGui::IsPopupOpen(const char* str_id) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = window->GetID(str_id); - return IsPopupIdOpen(id); + return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); } bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags) @@ -3557,7 +3554,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags ext ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = window->GetID(name); - if (!IsPopupIdOpen(id)) + if (!IsPopupOpen(id)) { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values return false; @@ -8483,7 +8480,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f); const bool hovered = IsHovered(frame_bb, id); - bool popup_open = IsPopupIdOpen(id); + bool popup_open = IsPopupOpen(id); bool popup_opened_now = false; const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); @@ -8507,7 +8504,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi if (g.IO.MouseClicked[0]) { ClearActiveID(); - if (IsPopupIdOpen(id)) + if (IsPopupOpen(id)) { ClosePopup(id); } @@ -8521,7 +8518,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi } bool value_changed = false; - if (IsPopupIdOpen(id)) + if (IsPopupOpen(id)) { // Size default to hold ~7 items if (height_in_items < 0) @@ -8867,7 +8864,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) ImGuiWindow* backed_focused_window = g.FocusedWindow; bool pressed; - bool menu_is_open = IsPopupIdOpen(id); + bool menu_is_open = IsPopupOpen(id); bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus")); if (menuset_is_open) g.FocusedWindow = window; @@ -8934,7 +8931,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_open = true; if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' want_close = true; - if (want_close && IsPopupIdOpen(id)) + if (want_close && IsPopupOpen(id)) ClosePopupToLevel(GImGui->CurrentPopupStack.Size); if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) diff --git a/imgui_internal.h b/imgui_internal.h index 2803e3509..170eb4598 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -746,6 +746,7 @@ namespace ImGui IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); + IMGUI_API bool IsPopupOpen(ImGuiID id); // NB: All position are in absolute pixels coordinates (never using window coordinates internally) // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. From 638d77c682ba504a219a2a9d6a0a7f28dd9dd410 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 14:19:48 +0800 Subject: [PATCH 07/20] Comments (#402) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index 3e901a1d0..4a014fed4 100644 --- a/imgui.h +++ b/imgui.h @@ -381,7 +381,7 @@ namespace ImGui IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Popups - IMGUI_API void OpenPopup(const char* str_id); // mark popup as open. popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). + IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (block interactions behind the modal window, can't close the modal window by clicking outside) From 5ea1865fdb2151e5fcdbbb273ba9b6b3513a5015 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 14:24:41 +0800 Subject: [PATCH 08/20] (api breaking) changed parameter order for BeginPopupContextWindow(), note that most uses relied on default parameters completely. --- imgui.cpp | 9 ++++++--- imgui.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 958e07013..1562290a0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -204,6 +204,7 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow(), note that most uses relied on default parameters completely. - 2017/08/13 (1.51) - renamed ImGuiCol_Columns_*** to ImGuiCol_Separator_*** - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete). - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). @@ -3598,9 +3599,10 @@ bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) return BeginPopup(str_id); } -bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button) +bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) { - if (!str_id) str_id = "window_context_menu"; + if (!str_id) + str_id = "window_context_menu"; if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button)) if (also_over_items || !IsAnyItemHovered()) OpenPopupEx(str_id, true); @@ -3609,7 +3611,8 @@ bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, in bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) { - if (!str_id) str_id = "void_context_menu"; + if (!str_id) + str_id = "void_context_menu"; if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button)) OpenPopupEx(str_id, true); return BeginPopup(str_id); diff --git a/imgui.h b/imgui.h index 4a014fed4..bbc66a337 100644 --- a/imgui.h +++ b/imgui.h @@ -386,7 +386,7 @@ namespace ImGui IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (block interactions behind the modal window, can't close the modal window by clicking outside) IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp! - IMGUI_API bool BeginPopupContextWindow(bool also_over_items = true, const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on current window. + IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (no window). IMGUI_API void EndPopup(); IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. From a9915681eb354096b62362d0d799ccf7cbe83080 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 14:37:54 +0800 Subject: [PATCH 09/20] PushID()/PopID() to not need to mark parent window as Accessed (needlessly waking up the root "Debug" window) (#747) --- imgui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1562290a0..dc7081bbb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6185,32 +6185,32 @@ void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond) void ImGui::PushID(const char* str_id) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GetCurrentWindowRead(); window->IDStack.push_back(window->GetID(str_id)); } void ImGui::PushID(const char* str_id_begin, const char* str_id_end) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GetCurrentWindowRead(); window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); } void ImGui::PushID(const void* ptr_id) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GetCurrentWindowRead(); window->IDStack.push_back(window->GetID(ptr_id)); } void ImGui::PushID(int int_id) { const void* ptr_id = (void*)(intptr_t)int_id; - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GetCurrentWindowRead(); window->IDStack.push_back(window->GetID(ptr_id)); } void ImGui::PopID() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GetCurrentWindowRead(); window->IDStack.pop_back(); } From a85a14370bf58accafb262486045ba1557df659c Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 15:47:10 +0800 Subject: [PATCH 10/20] OpenPopupEx() internal tweaks to receive an ImGuiID, BeginPopupContextXXX shortening unnecessarily long identifier. --- imgui.cpp | 20 +++++++++++--------- imgui.h | 2 +- imgui_internal.h | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dc7081bbb..13a7ca0ad 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3400,11 +3400,10 @@ void ImGui::EndTooltip() // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) +void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID(str_id); int current_stack_size = g.CurrentPopupStack.Size; ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) if (g.OpenPopupStack.Size < current_stack_size + 1) @@ -3418,7 +3417,8 @@ void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) void ImGui::OpenPopup(const char* str_id) { - ImGui::OpenPopupEx(str_id, false); + ImGuiContext& g = *GImGui; + OpenPopupEx(g.CurrentWindow->GetID(str_id), false); } static void CloseInactivePopups() @@ -3494,6 +3494,7 @@ void ImGui::CloseCurrentPopup() static inline void ClearSetNextWindowData() { + // FIXME-OPT ImGuiContext& g = *GImGui; g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0; g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false; @@ -3538,6 +3539,7 @@ bool ImGui::BeginPopup(const char* str_id) return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders); } +// FIXME bool ImGui::IsPopupOpen(ImGuiID id) { ImGuiContext& g = *GImGui; @@ -3590,31 +3592,31 @@ void ImGui::EndPopup() // 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect() // and passing true to the OpenPopupEx(). // Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that -// the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu +// the item can be interacted with (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu // driven by click position. bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) { if (IsItemHovered() && IsMouseClicked(mouse_button)) - OpenPopupEx(str_id, false); + OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), false); return BeginPopup(str_id); } bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) { if (!str_id) - str_id = "window_context_menu"; + str_id = "window_context"; if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button)) if (also_over_items || !IsAnyItemHovered()) - OpenPopupEx(str_id, true); + OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true); return BeginPopup(str_id); } bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) { if (!str_id) - str_id = "void_context_menu"; + str_id = "void_context"; if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button)) - OpenPopupEx(str_id, true); + OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true); return BeginPopup(str_id); } diff --git a/imgui.h b/imgui.h index bbc66a337..907d415de 100644 --- a/imgui.h +++ b/imgui.h @@ -382,13 +382,13 @@ namespace ImGui // Popups IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). - IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (block interactions behind the modal window, can't close the modal window by clicking outside) IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (no window). IMGUI_API void EndPopup(); + IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. // Logging: all text output from interface is redirected to tty/file/clipboard. By default, tree nodes are automatically opened during logging. diff --git a/imgui_internal.h b/imgui_internal.h index 170eb4598..0d8981278 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -745,7 +745,7 @@ namespace ImGui IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); - IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); + IMGUI_API void OpenPopupEx(ImGuiID id, bool reopen_existing); IMGUI_API bool IsPopupOpen(ImGuiID id); // NB: All position are in absolute pixels coordinates (never using window coordinates internally) From 68bf5ecbc1148d4dd61bdca0e7a8871b492a8e84 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 17:51:44 +0800 Subject: [PATCH 11/20] Marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame. Removed the broken __LINE__ from IMGUI_ONCE_UPON_A_FRAME --- imgui.cpp | 1 + imgui.h | 17 +++++++++-------- imgui_demo.cpp | 11 ++++------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 13a7ca0ad..5a935aac7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -204,6 +204,7 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame. - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow(), note that most uses relied on default parameters completely. - 2017/08/13 (1.51) - renamed ImGuiCol_Columns_*** to ImGuiCol_Separator_*** - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete). diff --git a/imgui.h b/imgui.h index 907d415de..f5c7dcc23 100644 --- a/imgui.h +++ b/imgui.h @@ -924,15 +924,11 @@ public: inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; } }; -// Helper: execute a block of code at maximum once a frame -// Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. +// Helper: execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. // Usage: -// IMGUI_ONCE_UPON_A_FRAME -// { -// // code block will be executed one per frame -// } -// Attention! the macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. -#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf##__LINE__; if (imgui_oaf##__LINE__) +// static ImGuiOnceUponAFrame oaf; +// if (oaf) +// ImGui::Text("This will be called only once per frame"); struct ImGuiOnceUponAFrame { ImGuiOnceUponAFrame() { RefFrame = -1; } @@ -940,6 +936,11 @@ struct ImGuiOnceUponAFrame operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } }; +// Helper macro for ImGuiOnceUponAFrame. Attention: The macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Will obsolete +#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf; if (imgui_oaf) +#endif + // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" struct ImGuiTextFilter { diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3a7691162..8e9eaebf9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -308,14 +308,11 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::EndTooltip(); } - // Testing IMGUI_ONCE_UPON_A_FRAME macro + // Testing ImGuiOnceUponAFrame helper. + //static ImGuiOnceUponAFrame once; //for (int i = 0; i < 5; i++) - //{ - // IMGUI_ONCE_UPON_A_FRAME - // { - // ImGui::Text("This will be displayed only once."); - // } - //} + // if (once) + // ImGui::Text("This will be displayed only once."); ImGui::Separator(); From fd9460a0877601ff3ed4a43fb2b92ce8fbef6fbb Mon Sep 17 00:00:00 2001 From: Viktor Kirilov Date: Wed, 16 Aug 2017 17:52:11 +0300 Subject: [PATCH 12/20] added missing IMGUI_API to ImGuiTextFilter methods implemented in imgui.cpp --- imgui.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.h b/imgui.h index f5c7dcc23..0e287fb1e 100644 --- a/imgui.h +++ b/imgui.h @@ -964,11 +964,11 @@ struct ImGuiTextFilter ImVector Filters; int CountGrep; - ImGuiTextFilter(const char* default_filter = ""); - ~ImGuiTextFilter() {} + IMGUI_API ImGuiTextFilter(const char* default_filter = ""); + ~ImGuiTextFilter() {} void Clear() { InputBuf[0] = 0; Build(); } - bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build - bool PassFilter(const char* text, const char* text_end = NULL) const; + IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build + IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; bool IsActive() const { return !Filters.empty(); } IMGUI_API void Build(); }; From 0be4f66d89a1130c04262844dcbef600e91dcb88 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Aug 2017 18:56:26 +0800 Subject: [PATCH 13/20] ImFontAtlas: Shuffling some code inside Build() to make upcoming diffs less confusing (nb: we might break compat with forks of Build() like #618) --- TODO.txt | 1 + imgui_draw.cpp | 60 +++++++++++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/TODO.txt b/TODO.txt index 61d9e048d..5f70cc960 100644 --- a/TODO.txt +++ b/TODO.txt @@ -24,6 +24,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. - window: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? - window: expose contents size. (#1045) + - window: GetWindowSize() returns (0,0) when not calculated? (#1045) !- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet. - scrolling/clipping: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6e94cfb6b..2c6d88e5a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1034,7 +1034,7 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale) } //----------------------------------------------------------------------------- -// ImFontAtlas +// ImFontConfig //----------------------------------------------------------------------------- ImFontConfig::ImFontConfig() @@ -1055,6 +1055,10 @@ ImFontConfig::ImFontConfig() memset(Name, 0, sizeof(Name)); } +//----------------------------------------------------------------------------- +// ImFontAtlas +//----------------------------------------------------------------------------- + ImFontAtlas::ImFontAtlas() { TexID = NULL; @@ -1282,40 +1286,19 @@ bool ImFontAtlas::Build() TexUvWhitePixel = ImVec2(0, 0); ClearTexData(); - struct ImFontTempBuildData - { - stbtt_fontinfo FontInfo; - stbrp_rect* Rects; - stbtt_pack_range* Ranges; - int RangesCount; - }; - ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); - - // Initialize font information early (so we can error without any cleanup) + count glyphs + // Count glyphs/ranges int total_glyph_count = 0; int total_glyph_range_count = 0; for (int input_i = 0; input_i < ConfigData.Size; input_i++) { ImFontConfig& cfg = ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - - IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this)); - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); - IM_ASSERT(font_offset >= 0); - if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) - return false; - - // Count glyphs if (!cfg.GlyphRanges) cfg.GlyphRanges = GetGlyphRangesDefault(); - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) - { + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_glyph_range_count++) total_glyph_count += (in_range[1] - in_range[0]) + 1; - total_glyph_range_count++; - } } - // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. + // Start packing. We need a known width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height. TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512; TexHeight = 0; @@ -1332,6 +1315,26 @@ bool ImFontAtlas::Build() if (extra_rects[i].was_packed) TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h); + // Initialize font information (so we can error without any cleanup) + struct ImFontTempBuildData + { + stbtt_fontinfo FontInfo; + stbrp_rect* Rects; + stbtt_pack_range* Ranges; + int RangesCount; + }; + ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this)); + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); + IM_ASSERT(font_offset >= 0); + if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) + return false; + } + // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar)); @@ -1350,11 +1353,8 @@ bool ImFontAtlas::Build() // Setup ranges int glyph_count = 0; int glyph_ranges_count = 0; - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) - { + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, glyph_ranges_count++) glyph_count += (in_range[1] - in_range[0]) + 1; - glyph_ranges_count++; - } tmp.Ranges = buf_ranges + buf_ranges_n; tmp.RangesCount = glyph_ranges_count; buf_ranges_n += glyph_ranges_count; @@ -1392,7 +1392,7 @@ bool ImFontAtlas::Build() spc.pixels = TexPixelsAlpha8; spc.height = TexHeight; - // Second pass: render characters + // Second pass: render font characters for (int input_i = 0; input_i < ConfigData.Size; input_i++) { ImFontConfig& cfg = ConfigData[input_i]; From c569676a7b58014fabf113e5a517012261748f76 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 11:37:07 +0800 Subject: [PATCH 14/20] ImVector: Added a const --- TODO.txt | 1 + imgui.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO.txt b/TODO.txt index 5f70cc960..eac6d7473 100644 --- a/TODO.txt +++ b/TODO.txt @@ -75,6 +75,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - layout: more generic alignment state (left/right/centered) for single items? - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding. - layout: BeginGroup() needs a border option. + - layout: vertical alignement of mixed height items (e.g. buttons) within a same line (#1284) - columns: sizing policy (e.g. for each column: fixed size, %, fill, distribute default size among fills) (#513, #125) - columns: add a conditional parameter to SetColumnOffset() (#513, #125) diff --git a/imgui.h b/imgui.h index 0e287fb1e..681ad9be5 100644 --- a/imgui.h +++ b/imgui.h @@ -903,7 +903,7 @@ public: inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size-1]; } inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } - inline int _grow_capacity(int new_size) { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > new_size ? new_capacity : new_size; } + inline int _grow_capacity(int size) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > size ? new_capacity : size; } inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } inline void reserve(int new_capacity) From 52f1a4124c1a3f104e1ee82bf62b469cddbcaccf Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 13:56:16 +0800 Subject: [PATCH 15/20] Demo: Tweaked Fonts section. --- imgui_demo.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8e9eaebf9..78e527f7a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1872,9 +1872,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::TreePop(); } - if (ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size)) + bool fonts_opened = ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size); + ImGui::SameLine(); ShowHelpMarker("Tip: Load fonts with io.Fonts->AddFontFromFileTTF()\nbefore calling io.Fonts->GetTex* functions."); + if (fonts_opened) { - ImGui::SameLine(); ShowHelpMarker("Tip: Load fonts with io.Fonts->AddFontFromFileTTF()\nbefore calling io.Fonts->GetTex* functions."); ImFontAtlas* atlas = ImGui::GetIO().Fonts; if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) { @@ -1885,14 +1886,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) for (int i = 0; i < atlas->Fonts.Size; i++) { ImFont* font = atlas->Fonts[i]; - ImGui::BulletText("Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); - ImGui::TreePush((void*)(intptr_t)i); + bool font_details_opened = ImGui::TreeNode(font, "Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) ImGui::GetIO().FontDefault = font; - ImGui::PushFont(font); - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::PopFont(); - if (ImGui::TreeNode("Details")) + if (font_details_opened) { + ImGui::PushFont(font); + ImGui::Text("The quick brown fox jumps over the lazy dog"); + ImGui::PopFont(); ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); @@ -1946,7 +1946,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } ImGui::TreePop(); } - ImGui::TreePop(); } static float window_scale = 1.0f; ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window From 4a7e1ff4d4f3b43bf510f40c3772e7adef5a3b82 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 15:35:59 +0800 Subject: [PATCH 16/20] ImFontAtlas: Some shallow renaming + added an assert for clarification --- imgui_draw.cpp | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2c6d88e5a..6f817db1b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1287,20 +1287,20 @@ bool ImFontAtlas::Build() ClearTexData(); // Count glyphs/ranges - int total_glyph_count = 0; - int total_glyph_range_count = 0; + int total_glyphs_count = 0; + int total_ranges_count = 0; for (int input_i = 0; input_i < ConfigData.Size; input_i++) { ImFontConfig& cfg = ConfigData[input_i]; if (!cfg.GlyphRanges) cfg.GlyphRanges = GetGlyphRangesDefault(); - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_glyph_range_count++) - total_glyph_count += (in_range[1] - in_range[0]) + 1; + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++) + total_glyphs_count += (in_range[1] - in_range[0]) + 1; } // Start packing. We need a known width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height. - TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512; + TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; TexHeight = 0; const int max_tex_height = 1024*32; stbtt_pack_context spc; @@ -1337,12 +1337,12 @@ bool ImFontAtlas::Build() // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; - stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar)); - stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect)); - stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range)); - memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar)); - memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. - memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); + stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar)); + stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect)); + stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range)); + memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar)); + memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. + memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range)); // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) for (int input_i = 0; input_i < ConfigData.Size; input_i++) @@ -1351,14 +1351,14 @@ bool ImFontAtlas::Build() ImFontTempBuildData& tmp = tmp_array[input_i]; // Setup ranges - int glyph_count = 0; - int glyph_ranges_count = 0; - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, glyph_ranges_count++) - glyph_count += (in_range[1] - in_range[0]) + 1; + int font_glyphs_count = 0; + int font_ranges_count = 0; + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++) + font_glyphs_count += (in_range[1] - in_range[0]) + 1; tmp.Ranges = buf_ranges + buf_ranges_n; - tmp.RangesCount = glyph_ranges_count; - buf_ranges_n += glyph_ranges_count; - for (int i = 0; i < glyph_ranges_count; i++) + tmp.RangesCount = font_ranges_count; + buf_ranges_n += font_ranges_count; + for (int i = 0; i < font_ranges_count; i++) { const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; stbtt_pack_range& range = tmp.Ranges[i]; @@ -1371,9 +1371,10 @@ bool ImFontAtlas::Build() // Pack tmp.Rects = buf_rects + buf_rects_n; - buf_rects_n += glyph_count; + buf_rects_n += font_glyphs_count; stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + IM_ASSERT(n == font_glyphs_count); stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); // Extend texture height @@ -1381,9 +1382,9 @@ bool ImFontAtlas::Build() if (tmp.Rects[i].was_packed) TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); } - IM_ASSERT(buf_rects_n == total_glyph_count); - IM_ASSERT(buf_packedchars_n == total_glyph_count); - IM_ASSERT(buf_ranges_n == total_glyph_range_count); + IM_ASSERT(buf_rects_n == total_glyphs_count); + IM_ASSERT(buf_packedchars_n == total_glyphs_count); + IM_ASSERT(buf_ranges_n == total_ranges_count); // Create texture TexHeight = ImUpperPowerOfTwo(TexHeight); From d970957e2de0780bec50ed05c9f412df6db08dad Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 19:36:48 +0800 Subject: [PATCH 17/20] ImFontAtlas: Draft of an api to submit custom rectangle (not exposed). Atlas default texture chunk using it. (WIP: we are still storing mouse UV outside in GImGui) --- imgui.h | 16 +++- imgui_draw.cpp | 224 +++++++++++++++++++++++++++++-------------------- 2 files changed, 145 insertions(+), 95 deletions(-) diff --git a/imgui.h b/imgui.h index 681ad9be5..19f01c85f 100644 --- a/imgui.h +++ b/imgui.h @@ -1388,10 +1388,22 @@ struct ImFontAtlas ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. - // Private + // [Private] User rectangle for packing custom texture data into the atlas. + struct CustomRect + { + unsigned int ID; // Input // User ID. <0x10000 for font mapped data (WIP/UNSUPPORTED), >=0x10000 for other texture data + unsigned short Width, Height; // Input // Desired rectangle dimension + unsigned short X, Y; // Output // Packed position in Atlas + CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; } + bool IsPacked() const { return X != 0xFFFF; } + }; + + // [Private] Members + ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Internal data IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. - IMGUI_API void RenderCustomTexData(int pass, void* rects); + IMGUI_API int CustomRectRegister(unsigned int id, int width, int height); + IMGUI_API void CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max); }; // Font runtime data and rendering diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6f817db1b..579fe6f0f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1059,6 +1059,42 @@ ImFontConfig::ImFontConfig() // ImFontAtlas //----------------------------------------------------------------------------- +// A work of art lies ahead! (. = white layer, X = black layer, others are blank) +// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. +const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90; +const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; +const int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0xF0000; +const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = +{ + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " +}; + ImFontAtlas::ImFontAtlas() { TexID = NULL; @@ -1091,6 +1127,7 @@ void ImFontAtlas::ClearInputData() Fonts[i]->ConfigDataCount = 0; } ConfigData.clear(); + CustomRects.clear(); } void ImFontAtlas::ClearTexData() @@ -1277,6 +1314,29 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed return font; } +int ImFontAtlas::CustomRectRegister(unsigned int id, int width, int height) +{ + IM_ASSERT(width > 0 && width <= 0xFFFF); + IM_ASSERT(height > 0 && height <= 0xFFFF); + CustomRect r; + r.ID = id; + r.Width = (unsigned short)width; + r.Height = (unsigned short)height; + CustomRects.push_back(r); + return CustomRects.Size - 1; // Return index +} + +void ImFontAtlas::CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) +{ + IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates + IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed + *out_uv_min = ImVec2((float)rect->X / TexWidth, (float)rect->Y / TexHeight); + *out_uv_max = ImVec2((float)(rect->X + rect->Width) / TexWidth, (float)(rect->Y + rect->Height) / TexHeight); +} + +static void BuildPackCustomRects(ImFontAtlas* atlas, stbtt_pack_context* spc); +static void BuildRenderDefaultTexData(ImFontAtlas* atlas); + bool ImFontAtlas::Build() { IM_ASSERT(ConfigData.Size > 0); @@ -1299,7 +1359,7 @@ bool ImFontAtlas::Build() } // Start packing. We need a known width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. - // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height. + // Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; TexHeight = 0; const int max_tex_height = 1024*32; @@ -1307,13 +1367,10 @@ bool ImFontAtlas::Build() stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, TexGlyphPadding, NULL); // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). - ImVector extra_rects; - RenderCustomTexData(0, &extra_rects); - stbtt_PackSetOversampling(&spc, 1, 1); - stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size); - for (int i = 0; i < extra_rects.Size; i++) - if (extra_rects[i].was_packed) - TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h); + // FIXME-WIP: We should register in the constructor (but cannot because our static instances may not have allocator ready by the time they initialize). This needs to be fixed because we can expose CustomRects. + if (CustomRects.empty()) + CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); + BuildPackCustomRects(this, &spc); // Initialize font information (so we can error without any cleanup) struct ImFontTempBuildData @@ -1476,100 +1533,81 @@ bool ImFontAtlas::Build() ImGui::MemFree(tmp_array); // Render into our custom data block - RenderCustomTexData(1, &extra_rects); + BuildRenderDefaultTexData(this); return true; } -void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) +static void BuildPackCustomRects(ImFontAtlas* atlas, stbtt_pack_context* spc) { - // A work of art lies ahead! (. = white layer, X = black layer, others are blank) - // The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. - const int TEX_DATA_W = 90; - const int TEX_DATA_H = 27; - const char texture_data[TEX_DATA_W*TEX_DATA_H+1] = + ImVector& user_rects = atlas->CustomRects; + ImVector pack_rects; + pack_rects.resize(user_rects.Size); + memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size); + for (int i = 0; i < user_rects.Size; i++) { - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" - "..- -X.....X- X.X - X.X -X.....X - X.....X" - "--- -XXX.XXX- X...X - X...X -X....X - X....X" - "X - X.X - X.....X - X.....X -X...X - X...X" - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" - "X..X - X.X - X.X - X.X -XX X.X - X.X XX" - "X...X - X.X - X.X - XX X.X XX - X.X - X.X " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" - "X.X X..X - -X.......X- X.......X - XX XX - " - "XX X..X - - X.....X - X.....X - X.X X.X - " - " X..X - X...X - X...X - X..X X..X - " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " - "------------ - X - X -X.....................X- " - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " + pack_rects[i].w = user_rects[i].Width; + pack_rects[i].h = user_rects[i].Height; + } + stbtt_PackSetOversampling(spc, 1, 1); + stbrp_pack_rects((stbrp_context*)spc->pack_info, &pack_rects[0], pack_rects.Size); + for (int i = 0; i < pack_rects.Size; i++) + if (pack_rects[i].was_packed) + { + user_rects[i].X = pack_rects[i].x; + user_rects[i].Y = pack_rects[i].y; + IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height); + atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h); + } +} + +static void BuildRenderDefaultTexData(ImFontAtlas* atlas) +{ + ImFontAtlas::CustomRect& r = atlas->CustomRects[0]; + IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID); + IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1); + IM_ASSERT(r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); + IM_ASSERT(r.IsPacked()); + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + + // Render/copy pixels + for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++) + for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++) + { + const int offset0 = (int)(r.X + x) + (int)(r.Y + y) * atlas->TexWidth; + const int offset1 = offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1; + atlas->TexPixelsAlpha8[offset0] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00; + atlas->TexPixelsAlpha8[offset1] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00; + } + const ImVec2 tex_uv_scale(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); + atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * tex_uv_scale.x, (r.Y + 0.5f) * tex_uv_scale.y); + + // Setup mouse cursors + const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = + { + // Pos ........ Size ......... Offset ...... + { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow + { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput + { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move + { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS + { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW + { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW + { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE }; - ImVector& rects = *(ImVector*)p_rects; - if (pass == 0) + for (int type = 0; type < ImGuiMouseCursor_Count_; type++) { - // Request rectangles - stbrp_rect r; - memset(&r, 0, sizeof(r)); - r.w = (TEX_DATA_W*2)+1; - r.h = TEX_DATA_H+1; - rects.push_back(r); - } - else if (pass == 1) - { - // Render/copy pixels - const stbrp_rect& r = rects[0]; - for (int y = 0, n = 0; y < TEX_DATA_H; y++) - for (int x = 0; x < TEX_DATA_W; x++, n++) - { - const int offset0 = (int)(r.x + x) + (int)(r.y + y) * TexWidth; - const int offset1 = offset0 + 1 + TEX_DATA_W; - TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00; - TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00; - } - const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight); - TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y); - - // Setup mouse cursors - const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = - { - // Pos ........ Size ......... Offset ...... - { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow - { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput - { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move - { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS - { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW - { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW - { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE - }; - - for (int type = 0; type < ImGuiMouseCursor_Count_; type++) - { - ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; - ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); - const ImVec2 size = cursor_datas[type][1]; - cursor_data.Type = type; - cursor_data.Size = size; - cursor_data.HotOffset = cursor_datas[type][2]; - cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; - cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; - pos.x += TEX_DATA_W+1; - cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; - cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; - } + ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; + ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.X, (float)r.Y); + const ImVec2 size = cursor_datas[type][1]; + cursor_data.Type = type; + cursor_data.Size = size; + cursor_data.HotOffset = cursor_datas[type][2]; + cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; + pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1; + cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; } } From 4075cc58e9e2ee53b69fc8a2ad8286b01c63811a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 20:44:44 +0800 Subject: [PATCH 18/20] ImFontAtlas; Re-arranging code to simplify implementation of imgui_freetype (#618) --- imgui_draw.cpp | 33 ++++++++++++++++++++------------- imgui_internal.h | 5 +++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 579fe6f0f..96f258c00 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1334,13 +1334,12 @@ void ImFontAtlas::CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, I *out_uv_max = ImVec2((float)(rect->X + rect->Width) / TexWidth, (float)(rect->Y + rect->Height) / TexHeight); } -static void BuildPackCustomRects(ImFontAtlas* atlas, stbtt_pack_context* spc); -static void BuildRenderDefaultTexData(ImFontAtlas* atlas); - bool ImFontAtlas::Build() { IM_ASSERT(ConfigData.Size > 0); + ImFontAtlasBuildRegisterDefaultCustomRects(this); + TexID = NULL; TexWidth = TexHeight = 0; TexUvWhitePixel = ImVec2(0, 0); @@ -1358,19 +1357,19 @@ bool ImFontAtlas::Build() total_glyphs_count += (in_range[1] - in_range[0]) + 1; } - // Start packing. We need a known width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. + // We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. // Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; TexHeight = 0; + + // Start packing const int max_tex_height = 1024*32; stbtt_pack_context spc; stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, TexGlyphPadding, NULL); + stbtt_PackSetOversampling(&spc, 1, 1); // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). - // FIXME-WIP: We should register in the constructor (but cannot because our static instances may not have allocator ready by the time they initialize). This needs to be fixed because we can expose CustomRects. - if (CustomRects.empty()) - CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); - BuildPackCustomRects(this, &spc); + ImFontAtlasBuildPackCustomRects(this, spc.pack_info); // Initialize font information (so we can error without any cleanup) struct ImFontTempBuildData @@ -1533,13 +1532,22 @@ bool ImFontAtlas::Build() ImGui::MemFree(tmp_array); // Render into our custom data block - BuildRenderDefaultTexData(this); + ImFontAtlasBuildRenderDefaultTexData(this); return true; } -static void BuildPackCustomRects(ImFontAtlas* atlas, stbtt_pack_context* spc) +void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas) { + // FIXME-WIP: We should register in the constructor (but cannot because our static instances may not have allocator ready by the time they initialize). This needs to be fixed because we can expose CustomRects. + if (atlas->CustomRects.empty()) + atlas->CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); +} + +void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque) +{ + stbrp_context* pack_context = (stbrp_context*)pack_context_opaque; + ImVector& user_rects = atlas->CustomRects; ImVector pack_rects; pack_rects.resize(user_rects.Size); @@ -1549,8 +1557,7 @@ static void BuildPackCustomRects(ImFontAtlas* atlas, stbtt_pack_context* spc) pack_rects[i].w = user_rects[i].Width; pack_rects[i].h = user_rects[i].Height; } - stbtt_PackSetOversampling(spc, 1, 1); - stbrp_pack_rects((stbrp_context*)spc->pack_info, &pack_rects[0], pack_rects.Size); + stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size); for (int i = 0; i < pack_rects.Size; i++) if (pack_rects[i].was_packed) { @@ -1561,7 +1568,7 @@ static void BuildPackCustomRects(ImFontAtlas* atlas, stbtt_pack_context* spc) } } -static void BuildRenderDefaultTexData(ImFontAtlas* atlas) +void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) { ImFontAtlas::CustomRect& r = atlas->CustomRects[0]; IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID); diff --git a/imgui_internal.h b/imgui_internal.h index 0d8981278..310878049 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -792,6 +792,11 @@ namespace ImGui } // namespace ImGui +// ImFontAtlas +IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); +IMGUI_API void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas); + #ifdef __clang__ #pragma clang diagnostic pop #endif From 1086c877671eabd394fbb178de88b446bfd04528 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 21:13:14 +0800 Subject: [PATCH 19/20] ImFontAtlas: Re-arranging code to simplify implementation of imgui_freetype. (#618) --- imgui_draw.cpp | 44 +++++++++++++++++++++++++++----------------- imgui_internal.h | 3 ++- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 96f258c00..7f9ff56ec 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1477,20 +1477,9 @@ bool ImFontAtlas::Build() float ascent = unscaled_ascent * font_scale; float descent = unscaled_descent * font_scale; - if (!cfg.MergeMode) - { - dst_font->ContainerAtlas = this; - dst_font->ConfigData = &cfg; - dst_font->ConfigDataCount = 0; - dst_font->FontSize = cfg.SizePixels; - dst_font->Ascent = ascent; - dst_font->Descent = descent; - dst_font->Glyphs.resize(0); - dst_font->MetricsTotalSurface = 0; - } - dst_font->ConfigDataCount++; + ImFontAtlasBuildSetupFont(this, dst_font, &cfg, ascent, descent); float off_x = cfg.GlyphOffset.x; - float off_y = cfg.GlyphOffset.y; + float off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable() for (int i = 0; i < tmp.RangesCount; i++) @@ -1513,11 +1502,16 @@ bool ImFontAtlas::Build() dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); ImFont::Glyph& glyph = dst_font->Glyphs.back(); glyph.Codepoint = (ImWchar)codepoint; - glyph.X0 = q.x0 + off_x; glyph.Y0 = q.y0 + off_y; glyph.X1 = q.x1 + off_x; glyph.Y1 = q.y1 + off_y; - glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1; - glyph.Y0 += (float)(int)(dst_font->Ascent + 0.5f); - glyph.Y1 += (float)(int)(dst_font->Ascent + 0.5f); + glyph.X0 = q.x0 + off_x; + glyph.Y0 = q.y0 + off_y; + glyph.X1 = q.x1 + off_x; + glyph.Y1 = q.y1 + off_y; + glyph.U0 = q.s0; + glyph.V0 = q.t0; + glyph.U1 = q.s1; + glyph.V1 = q.t1; glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance + if (cfg.PixelSnapH) glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round @@ -1544,6 +1538,22 @@ void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas) atlas->CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); } +void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) +{ + if (!font_config->MergeMode) + { + font->ContainerAtlas = atlas; + font->ConfigData = font_config; + font->ConfigDataCount = 0; + font->FontSize = font_config->SizePixels; + font->Ascent = ascent; + font->Descent = descent; + font->Glyphs.resize(0); + font->MetricsTotalSurface = 0; + } + font->ConfigDataCount++; +} + void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque) { stbrp_context* pack_context = (stbrp_context*)pack_context_opaque; diff --git a/imgui_internal.h b/imgui_internal.h index 310878049..09f696282 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -792,8 +792,9 @@ namespace ImGui } // namespace ImGui -// ImFontAtlas +// ImFontAtlas internals IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); IMGUI_API void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas); From 8be7a60f206899ab66a280582d45505176d18945 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Aug 2017 21:19:54 +0800 Subject: [PATCH 20/20] ImFontAtlas: Re-arranging code to simplify implementation of imgui_freetype. (#618) --- imgui_draw.cpp | 72 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 1 + 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 7f9ff56ec..bacbd7f5d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1336,40 +1336,45 @@ void ImFontAtlas::CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, I bool ImFontAtlas::Build() { - IM_ASSERT(ConfigData.Size > 0); + return ImFontAtlasBuildWithStbTruetype(this); +} - ImFontAtlasBuildRegisterDefaultCustomRects(this); +bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) +{ + IM_ASSERT(atlas->ConfigData.Size > 0); - TexID = NULL; - TexWidth = TexHeight = 0; - TexUvWhitePixel = ImVec2(0, 0); - ClearTexData(); + ImFontAtlasBuildRegisterDefaultCustomRects(atlas); + + atlas->TexID = NULL; + atlas->TexWidth = atlas->TexHeight = 0; + atlas->TexUvWhitePixel = ImVec2(0, 0); + atlas->ClearTexData(); // Count glyphs/ranges int total_glyphs_count = 0; int total_ranges_count = 0; - for (int input_i = 0; input_i < ConfigData.Size; input_i++) + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) { - ImFontConfig& cfg = ConfigData[input_i]; + ImFontConfig& cfg = atlas->ConfigData[input_i]; if (!cfg.GlyphRanges) - cfg.GlyphRanges = GetGlyphRangesDefault(); + cfg.GlyphRanges = atlas->GetGlyphRangesDefault(); for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++) total_glyphs_count += (in_range[1] - in_range[0]) + 1; } // We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. // Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. - TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; - TexHeight = 0; + atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; + atlas->TexHeight = 0; // Start packing const int max_tex_height = 1024*32; stbtt_pack_context spc; - stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, TexGlyphPadding, NULL); + stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL); stbtt_PackSetOversampling(&spc, 1, 1); // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). - ImFontAtlasBuildPackCustomRects(this, spc.pack_info); + ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); // Initialize font information (so we can error without any cleanup) struct ImFontTempBuildData @@ -1379,12 +1384,13 @@ bool ImFontAtlas::Build() stbtt_pack_range* Ranges; int RangesCount; }; - ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); - for (int input_i = 0; input_i < ConfigData.Size; input_i++) + ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData)); + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) { - ImFontConfig& cfg = ConfigData[input_i]; + ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i]; - IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this)); + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); IM_ASSERT(font_offset >= 0); if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) @@ -1401,9 +1407,9 @@ bool ImFontAtlas::Build() memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range)); // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) - for (int input_i = 0; input_i < ConfigData.Size; input_i++) + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) { - ImFontConfig& cfg = ConfigData[input_i]; + ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i]; // Setup ranges @@ -1436,23 +1442,23 @@ bool ImFontAtlas::Build() // Extend texture height for (int i = 0; i < n; i++) if (tmp.Rects[i].was_packed) - TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); + atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); } IM_ASSERT(buf_rects_n == total_glyphs_count); IM_ASSERT(buf_packedchars_n == total_glyphs_count); IM_ASSERT(buf_ranges_n == total_ranges_count); // Create texture - TexHeight = ImUpperPowerOfTwo(TexHeight); - TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(TexWidth * TexHeight); - memset(TexPixelsAlpha8, 0, TexWidth * TexHeight); - spc.pixels = TexPixelsAlpha8; - spc.height = TexHeight; + atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight); + atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); + memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); + spc.pixels = atlas->TexPixelsAlpha8; + spc.height = atlas->TexHeight; // Second pass: render font characters - for (int input_i = 0; input_i < ConfigData.Size; input_i++) + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) { - ImFontConfig& cfg = ConfigData[input_i]; + ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i]; stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); @@ -1465,9 +1471,9 @@ bool ImFontAtlas::Build() buf_rects = NULL; // Third pass: setup ImFont and glyphs for runtime - for (int input_i = 0; input_i < ConfigData.Size; input_i++) + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) { - ImFontConfig& cfg = ConfigData[input_i]; + ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i]; ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true) @@ -1477,7 +1483,7 @@ bool ImFontAtlas::Build() float ascent = unscaled_ascent * font_scale; float descent = unscaled_descent * font_scale; - ImFontAtlasBuildSetupFont(this, dst_font, &cfg, ascent, descent); + ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); float off_x = cfg.GlyphOffset.x; float off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); @@ -1497,7 +1503,7 @@ bool ImFontAtlas::Build() stbtt_aligned_quad q; float dummy_x = 0.0f, dummy_y = 0.0f; - stbtt_GetPackedQuad(range.chardata_for_range, TexWidth, TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); + stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); ImFont::Glyph& glyph = dst_font->Glyphs.back(); @@ -1514,7 +1520,7 @@ bool ImFontAtlas::Build() if (cfg.PixelSnapH) glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); - dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round + dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * atlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * atlas->TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round } } cfg.DstFont->BuildLookupTable(); @@ -1526,7 +1532,7 @@ bool ImFontAtlas::Build() ImGui::MemFree(tmp_array); // Render into our custom data block - ImFontAtlasBuildRenderDefaultTexData(this); + ImFontAtlasBuildRenderDefaultTexData(atlas); return true; } diff --git a/imgui_internal.h b/imgui_internal.h index 09f696282..31ae2613c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -793,6 +793,7 @@ namespace ImGui } // namespace ImGui // ImFontAtlas internals +IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc);