diff --git a/TODO.txt b/TODO.txt index 56eb873c..eac6d747 100644 --- a/TODO.txt +++ b/TODO.txt @@ -23,10 +23,13 @@ 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) + - 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) - 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? @@ -36,7 +39,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - 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. @@ -66,11 +69,13 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - 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? - 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) @@ -161,11 +166,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? @@ -174,6 +180,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 @@ -208,12 +215,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.cpp b/imgui.cpp index 2b83f210..2daf666c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -204,6 +204,8 @@ 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). - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). @@ -614,11 +616,10 @@ 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); -static bool IsPopupOpen(ImGuiID id); static ImGuiWindow* GetFrontMostModalRootWindow(); static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid); @@ -3404,21 +3405,14 @@ void ImGui::EndTooltip() ImGui::End(); } -static bool IsPopupOpen(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). // 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) @@ -3432,7 +3426,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() @@ -3488,7 +3483,7 @@ static void ClosePopupToLevel(int remaining) static void ClosePopup(ImGuiID id) { - if (!IsPopupOpen(id)) + if (!ImGui::IsPopupOpen(id)) return; ImGuiContext& g = *GImGui; ClosePopupToLevel(g.OpenPopupStack.Size - 1); @@ -3508,17 +3503,17 @@ 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; } -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)) + if (!ImGui::IsPopupOpen(id)) { ClearSetNextWindowData(); // We behave like Begin() and need to consume those values return false; @@ -3544,12 +3539,26 @@ 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); +} + +// FIXME +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; + 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) @@ -3592,29 +3601,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(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"; 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"; + if (!str_id) + str_id = "void_context"; if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button)) - OpenPopupEx(str_id, true); + OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true); return BeginPopup(str_id); } @@ -6187,32 +6198,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(); } @@ -8550,7 +8561,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(); @@ -8960,7 +8971,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; diff --git a/imgui.h b/imgui.h index 9fc13dc5..9410afd1 100644 --- a/imgui.h +++ b/imgui.h @@ -381,13 +381,14 @@ 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 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 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. @@ -905,7 +906,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 resize(int new_size, const T& v){ if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) Data[n] = v; Size = new_size; } @@ -927,15 +928,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; } @@ -943,6 +940,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 { @@ -966,11 +968,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(); }; @@ -1386,6 +1388,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_demo.cpp b/imgui_demo.cpp index ab417a01..a23c554b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -307,14 +307,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(); @@ -1874,9 +1871,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)) { @@ -1887,14 +1885,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); @@ -1948,7 +1945,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 diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 850f4880..7ae1ff47 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1034,7 +1034,7 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale) } //----------------------------------------------------------------------------- -// ImFontAtlas +// ImFontConfig //----------------------------------------------------------------------------- ImFontConfig::ImFontConfig() @@ -1055,12 +1055,17 @@ ImFontConfig::ImFontConfig() memset(Name, 0, sizeof(Name)); } +//----------------------------------------------------------------------------- +// ImFontAtlas +//----------------------------------------------------------------------------- + ImFontAtlas::ImFontAtlas() { TexID = NULL; TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; TexWidth = TexHeight = TexDesiredWidth = 0; + TexGlyphPadding = 1; TexUvWhitePixel = ImVec2(0, 0); } @@ -1281,46 +1286,25 @@ 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 - int total_glyph_count = 0; - int total_glyph_range_count = 0; + // Count glyphs/ranges + 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]; - 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) - { - total_glyph_count += (in_range[1] - in_range[0]) + 1; - total_glyph_range_count++; - } + 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 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 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; - 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; @@ -1331,14 +1315,34 @@ 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)); - 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++) @@ -1347,17 +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_count += (in_range[1] - in_range[0]) + 1; - glyph_ranges_count++; - } + 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]; @@ -1370,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 @@ -1380,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); @@ -1391,7 +1393,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]; diff --git a/imgui_internal.h b/imgui_internal.h index 5afb368e..0d898127 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 @@ -743,7 +745,8 @@ 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) // 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.