From 9c8fb804ed7d1523711b1ea460a380fc665292c3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 17:41:26 +0100 Subject: [PATCH 01/22] Log: Comments and extraneous assets to clarify intent. (#1584) --- imgui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 45e0e2a9..10250545 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6570,8 +6570,9 @@ void ImGui::LogToTTY(int max_depth) return; ImGuiWindow* window = g.CurrentWindow; - g.LogEnabled = true; + IM_ASSERT(g.LogFile == NULL); g.LogFile = stdout; + g.LogEnabled = true; g.LogStartDepth = window->DC.TreeDepth; if (max_depth >= 0) g.LogAutoExpandMaxDepth = max_depth; @@ -6592,6 +6593,7 @@ void ImGui::LogToFile(int max_depth, const char* filename) return; } + IM_ASSERT(g.LogFile == NULL); g.LogFile = ImFileOpen(filename, "ab"); if (!g.LogFile) { @@ -6612,8 +6614,9 @@ void ImGui::LogToClipboard(int max_depth) return; ImGuiWindow* window = g.CurrentWindow; - g.LogEnabled = true; + IM_ASSERT(g.LogFile == NULL); g.LogFile = NULL; + g.LogEnabled = true; g.LogStartDepth = window->DC.TreeDepth; if (max_depth >= 0) g.LogAutoExpandMaxDepth = max_depth; @@ -6626,7 +6629,6 @@ void ImGui::LogFinish() return; LogText(IM_NEWLINE); - g.LogEnabled = false; if (g.LogFile != NULL) { if (g.LogFile == stdout) @@ -6640,6 +6642,7 @@ void ImGui::LogFinish() SetClipboardText(g.LogClipboard->begin()); g.LogClipboard->clear(); } + g.LogEnabled = false; } // Helper to display logging buttons From dbdbf01b9f735ccf53f7e767e54ff12867eb617b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 19:13:20 +0100 Subject: [PATCH 02/22] Demo: Tweaked the Child demos, added a menu bar to the second child to test some navigation functions. --- imgui_demo.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 4c2a437e..b7c69948 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1051,9 +1051,10 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Child regions")) { static bool disable_mouse_wheel = false; + static bool disable_menu = false; ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); + ImGui::Checkbox("Disable Menu", &disable_menu); - ImGui::Text("Without border"); static int line = 50; bool goto_line = ImGui::Button("Goto"); ImGui::SameLine(); @@ -1080,8 +1081,16 @@ void ImGui::ShowDemoWindow(bool* p_open) // Child 2: rounded border { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); - ImGui::Text("With border"); + ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar)); + if (!disable_menu && ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Menu")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } ImGui::Columns(2); for (int i = 0; i < 100; i++) { From 9bc5c089b6c351b821bbd672f3c002b9b53ead03 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 22:14:33 +0100 Subject: [PATCH 03/22] Internal: Avoid needlessly bringing parent of front-most child back to front. This is mostly to reduce flicker/confusion in Metrics when traversing windows. We could aim at separating the child windows from non-child windows at some point. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 10250545..f0414555 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5161,7 +5161,8 @@ void ImGui::Scrollbar(ImGuiLayoutType direction) void ImGui::BringWindowToFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - if (g.Windows.back() == window) + ImGuiWindow* current_front_window = g.Windows.back(); + if (current_front_window == window || current_front_window->RootWindow == window) return; for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window if (g.Windows[i] == window) From 07163804df6312590900bc2d3d6edd412fc0dee6 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 18:31:41 +0100 Subject: [PATCH 04/22] Internals: Renamed CloseInactivePopups() to a better ClosePopupsOverWindow(). Exposed in imgui_internal.h --- imgui.cpp | 11 +++++------ imgui_internal.h | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f0414555..9d875577 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -669,7 +669,6 @@ static void MarkIniSettingsDirty(ImGuiWindow* window); static ImRect GetViewportRect(); -static void CloseInactivePopups(ImGuiWindow* ref_window); static void ClosePopupToLevel(int remaining); static ImGuiWindow* GetFrontMostModalRootWindow(); @@ -2549,7 +2548,7 @@ void ImGui::NewFrame() // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. g.CurrentWindowStack.resize(0); g.CurrentPopupStack.resize(0); - CloseInactivePopups(g.NavWindow); + ClosePopupsOverWindow(g.NavWindow); // Create implicit window - we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -3000,7 +2999,7 @@ void ImGui::EndFrame() } // With right mouse button we close popups without changing focus - // (The left mouse button path calls FocusWindow which will lead NewFrame->CloseInactivePopups to trigger) + // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) if (g.IO.MouseClicked[1]) { // Find the top-most window between HoveredWindow and the front most Modal Window. @@ -3017,7 +3016,7 @@ void ImGui::EndFrame() if (window == g.HoveredWindow) hovered_window_above_modal = true; } - CloseInactivePopups(hovered_window_above_modal ? g.HoveredWindow : modal); + ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); } } } @@ -3772,7 +3771,7 @@ void ImGui::OpenPopupEx(ImGuiID id) else g.OpenPopupStack[current_stack_size] = popup_ref; - // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by CloseInactivePopups(). + // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). // This is equivalent to what ClosePopupToLevel() does. //if (g.OpenPopupStack[current_stack_size].PopupId == id) // FocusWindow(parent_window); @@ -3785,7 +3784,7 @@ void ImGui::OpenPopup(const char* str_id) OpenPopupEx(g.CurrentWindow->GetID(str_id)); } -static void CloseInactivePopups(ImGuiWindow* ref_window) +void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) { ImGuiContext& g = *GImGui; if (g.OpenPopupStack.empty()) diff --git a/imgui_internal.h b/imgui_internal.h index a00a88a0..37a31bff 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -894,6 +894,7 @@ namespace ImGui IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void ClosePopup(ImGuiID id); + IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); IMGUI_API bool IsPopupOpen(ImGuiID id); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); From c4fc87950897ca5f0a1861c4f3d2a60d1f2f331a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 22:32:52 +0100 Subject: [PATCH 05/22] CloseButton: Fixed cross positioning. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 9d875577..fe71f4f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6458,12 +6458,13 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); - const ImVec2 center = bb.GetCenter(); + ImVec2 center = bb.GetCenter(); window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12); const float cross_extent = (radius * 0.7071f) - 1.0f; if (hovered) { + center -= ImVec2(0.5f, 0.5f); window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); } From 6a83a9152fe5ee6c59948099a6a2805ae1ae958f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 23:39:02 +0100 Subject: [PATCH 06/22] Natvis: Showing some flags for ImGuiWindow. --- misc/natvis/imgui.natvis | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/natvis/imgui.natvis b/misc/natvis/imgui.natvis index 38c676c7..48b20426 100644 --- a/misc/natvis/imgui.natvis +++ b/misc/natvis/imgui.natvis @@ -33,8 +33,7 @@ - {{Name={Name,s}}} - {{Name={Name,s}} Inactive} + {{Name={Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags & 0x01000000)?1:0,d} Popup {(Flags & 0x04000000)?1:0,d}} \ No newline at end of file From 147ec8d1e2a0935082207aeab254df7039bd29c5 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 4 Feb 2018 12:17:35 +0100 Subject: [PATCH 07/22] Style Added style.MouseCursorScale, may remove (#939). --- imgui.cpp | 13 ++++++++----- imgui.h | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fe71f4f0..9dd9eb77 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -774,6 +774,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) GrabRounding = ImFloor(GrabRounding * scale_factor); DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); + MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); } ImGuiIO::ImGuiIO() @@ -3073,11 +3074,12 @@ void ImGui::Render() { const ImVec2 pos = g.IO.MousePos - offset; const ImTextureID tex_id = g.IO.Fonts->TexID; + const float sc = g.Style.MouseCursorScale; g.OverlayDrawList.PushTextureID(tex_id); - g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos, pos + size, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border - g.OverlayDrawList.AddImage(tex_id, pos, pos + size, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill + g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow + g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow + g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border + g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill g.OverlayDrawList.PopTextureID(); } if (!g.OverlayDrawList.VtxBuffer.empty()) @@ -4723,8 +4725,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Position tooltip (always follows mouse) if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) { + float sc = g.Style.MouseCursorScale; ImVec2 ref_pos = g.IO.MousePos; - ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Store boxes in mouse cursor data? Scale? Center on cursor hit-point? + ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); if (window->AutoPosLastDirection == ImGuiDir_None) window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. diff --git a/imgui.h b/imgui.h index 4166b603..5c436c02 100644 --- a/imgui.h +++ b/imgui.h @@ -869,6 +869,7 @@ struct ImGuiStyle ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. From e9a617b22ad11117ae5e2e580f7d8d824949776c Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 4 Feb 2018 12:35:17 +0100 Subject: [PATCH 08/22] Moved imgui_freetype from imgui_club (#618) --- misc/freetype/imgui_freetype.cpp | 369 +++++++++++++++++++++++++++++++ misc/freetype/imgui_freetype.h | 31 +++ 2 files changed, 400 insertions(+) create mode 100644 misc/freetype/imgui_freetype.cpp create mode 100644 misc/freetype/imgui_freetype.h diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp new file mode 100644 index 00000000..968d7f17 --- /dev/null +++ b/misc/freetype/imgui_freetype.cpp @@ -0,0 +1,369 @@ +// Wrapper to use Freetype (instead of stb_truetype) for Dear ImGui +// Get latest version at http://www.github.com/ocornut/imgui_club +// Original code by @Vuhdo (Aleksei Skriabin) + +// Changelog: +// - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype, updated for latest changes in ImFontAtlas, minor tweaks. +// - v0.51: (2017/08/26) cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. +// - v0.52: (2017/09/26) fixes for imgui internal changes +// - v0.53: (2017/10/22) minor inconsequential change to match change in master (removed an unnecessary statement) +// - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member + +// Todo/Bugs: +// - Font size has lots of waste. +// - FreeType's memory allocator is not overridden. + +#include "imgui_freetype.h" +#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*, +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_SYNTHESIS_H + +#ifdef _MSC_VER +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#endif + +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#endif + +namespace +{ + // Glyph metrics: + // -------------- + // + // xmin xmax + // | | + // |<-------- width -------->| + // | | + // | +-------------------------+----------------- ymax + // | | ggggggggg ggggg | ^ ^ + // | | g:::::::::ggg::::g | | | + // | | g:::::::::::::::::g | | | + // | | g::::::ggggg::::::gg | | | + // | | g:::::g g:::::g | | | + // offsetX -|-------->| g:::::g g:::::g | offsetY | + // | | g:::::g g:::::g | | | + // | | g::::::g g:::::g | | | + // | | g:::::::ggggg:::::g | | | + // | | g::::::::::::::::g | | height + // | | gg::::::::::::::g | | | + // baseline ---*---------|---- gggggggg::::::g-----*-------- | + // / | | g:::::g | | + // origin | | gggggg g:::::g | | + // | | g:::::gg gg:::::g | | + // | | g::::::ggg:::::::g | | + // | | gg:::::::::::::g | | + // | | ggg::::::ggg | | + // | | gggggg | v + // | +-------------------------+----------------- ymin + // | | + // |------------- advanceX ----------->| + + /// A structure that describe a glyph. + struct GlyphInfo + { + float Width; // Glyph's width in pixels. + float Height; // Glyph's height in pixels. + float OffsetX; // The distance from the origin ("pen position") to the left of the glyph. + float OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0. + float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0. + }; + + // Font parameters and metrics. + struct FontInfo + { + uint32_t PixelHeight; // Size this font was generated with. + float Ascender; // The pixel extents above the baseline in pixels (typically positive). + float Descender; // The extents below the baseline in pixels (typically negative). + float LineSpacing; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate. + float LineGap; // The spacing in pixels between one row's descent and the next row's ascent. + float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font. + }; + + // FreeType glyph rasterizer. + // NB: No ctor/dtor, explicitly call Init()/Shutdown() + struct FreeTypeFont + { + bool Init(const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime. + void Shutdown(); + void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size + + bool CalcGlyphInfo(uint32_t codepoint, GlyphInfo& glyph_info, FT_Glyph& ft_glyph, FT_BitmapGlyph& ft_bitmap); + void BlitGlyph(FT_BitmapGlyph ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL); + + // [Internals] + FontInfo Info; // Font descriptor of the current font. + unsigned int UserFlags; // = ImFontConfig::RasterizerFlags + FT_Library FreetypeLibrary; + FT_Face FreetypeFace; + FT_Int32 FreetypeLoadFlags; + }; + + // From SDL_ttf: Handy routines for converting from fixed point + #define FT_CEIL(X) (((X + 63) & -64) / 64) + + bool FreeTypeFont::Init(const ImFontConfig& cfg, unsigned int extra_user_flags) + { + // FIXME: substitute allocator + FT_Error error = FT_Init_FreeType(&FreetypeLibrary); + if (error != 0) + return false; + error = FT_New_Memory_Face(FreetypeLibrary, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &FreetypeFace); + if (error != 0) + return false; + error = FT_Select_Charmap(FreetypeFace, FT_ENCODING_UNICODE); + if (error != 0) + return false; + + memset(&Info, 0, sizeof(Info)); + SetPixelHeight((uint32_t)cfg.SizePixels); + + // Convert to freetype flags (nb: Bold and Oblique are processed separately) + UserFlags = cfg.RasterizerFlags | extra_user_flags; + FreetypeLoadFlags = FT_LOAD_NO_BITMAP; + if (UserFlags & ImGuiFreeType::NoHinting) FreetypeLoadFlags |= FT_LOAD_NO_HINTING; + if (UserFlags & ImGuiFreeType::NoAutoHint) FreetypeLoadFlags |= FT_LOAD_NO_AUTOHINT; + if (UserFlags & ImGuiFreeType::ForceAutoHint) FreetypeLoadFlags |= FT_LOAD_FORCE_AUTOHINT; + if (UserFlags & ImGuiFreeType::LightHinting) + FreetypeLoadFlags |= FT_LOAD_TARGET_LIGHT; + else if (UserFlags & ImGuiFreeType::MonoHinting) + FreetypeLoadFlags |= FT_LOAD_TARGET_MONO; + else + FreetypeLoadFlags |= FT_LOAD_TARGET_NORMAL; + + return true; + } + + void FreeTypeFont::Shutdown() + { + if (FreetypeFace) + { + FT_Done_Face(FreetypeFace); + FreetypeFace = NULL; + FT_Done_FreeType(FreetypeLibrary); + FreetypeLibrary = NULL; + } + } + + void FreeTypeFont::SetPixelHeight(int pixel_height) + { + // I'm not sure how to deal with font sizes properly. + // As far as I understand, currently ImGui assumes that the 'pixel_height' is a maximum height of an any given glyph, + // i.e. it's the sum of font's ascender and descender. Seems strange to me. + FT_Size_RequestRec req; + req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; + req.width = 0; + req.height = (uint32_t)pixel_height * 64; + req.horiResolution = 0; + req.vertResolution = 0; + FT_Request_Size(FreetypeFace, &req); + + // update font info + FT_Size_Metrics metrics = FreetypeFace->size->metrics; + Info.PixelHeight = (uint32_t)pixel_height; + Info.Ascender = (float)FT_CEIL(metrics.ascender); + Info.Descender = (float)FT_CEIL(metrics.descender); + Info.LineSpacing = (float)FT_CEIL(metrics.height); + Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender); + Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance); + } + + bool FreeTypeFont::CalcGlyphInfo(uint32_t codepoint, GlyphInfo &glyph_info, FT_Glyph& ft_glyph, FT_BitmapGlyph& ft_bitmap) + { + uint32_t glyph_index = FT_Get_Char_Index(FreetypeFace, codepoint); + FT_Error error = FT_Load_Glyph(FreetypeFace, glyph_index, FreetypeLoadFlags); + if (error) + return false; + + // Need an outline for this to work + FT_GlyphSlot slot = FreetypeFace->glyph; + IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE); + + if (UserFlags & ImGuiFreeType::Bold) + FT_GlyphSlot_Embolden(slot); + if (UserFlags & ImGuiFreeType::Oblique) + FT_GlyphSlot_Oblique(slot); + + // Retrieve the glyph + error = FT_Get_Glyph(slot, &ft_glyph); + if (error != 0) + return false; + + // Rasterize + error = FT_Glyph_To_Bitmap(&ft_glyph, FT_RENDER_MODE_NORMAL, NULL, true); + if (error != 0) + return false; + + ft_bitmap = (FT_BitmapGlyph)ft_glyph; + glyph_info.AdvanceX = (float)FT_CEIL(slot->advance.x); + glyph_info.OffsetX = (float)ft_bitmap->left; + glyph_info.OffsetY = -(float)ft_bitmap->top; + glyph_info.Width = (float)ft_bitmap->bitmap.width; + glyph_info.Height = (float)ft_bitmap->bitmap.rows; + + return true; + } + + void FreeTypeFont::BlitGlyph(FT_BitmapGlyph ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table) + { + IM_ASSERT(ft_bitmap != NULL); + + const uint32_t w = ft_bitmap->bitmap.width; + const uint32_t h = ft_bitmap->bitmap.rows; + const uint8_t* src = ft_bitmap->bitmap.buffer; + const uint32_t src_pitch = ft_bitmap->bitmap.pitch; + + if (multiply_table == NULL) + { + for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) + memcpy(dst, src, w); + } + else + { + for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) + for (uint32_t x = 0; x < w; x++) + dst[x] = multiply_table[src[x]]; + } + } +} + +#define STBRP_ASSERT(x) IM_ASSERT(x) +#define STBRP_STATIC +#define STB_RECT_PACK_IMPLEMENTATION +#include "stb_rect_pack.h" + +bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags) +{ + IM_ASSERT(atlas->ConfigData.Size > 0); + IM_ASSERT(atlas->TexGlyphPadding == 1); // Not supported + + ImFontAtlasBuildRegisterDefaultCustomRects(atlas); + + atlas->TexID = NULL; + atlas->TexWidth = atlas->TexHeight = 0; + atlas->TexUvScale = ImVec2(0.0f, 0.0f); + atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); + atlas->ClearTexData(); + + ImVector fonts; + fonts.resize(atlas->ConfigData.Size); + + ImVec2 max_glyph_size(1.0f, 1.0f); + + // Count glyphs/ranges, initialize font + int total_glyphs_count = 0; + int total_ranges_count = 0; + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + { + ImFontConfig& cfg = atlas->ConfigData[input_i]; + FreeTypeFont& font_face = fonts[input_i]; + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); + + if (!font_face.Init(cfg, extra_flags)) + return false; + + max_glyph_size.x = ImMax(max_glyph_size.x, font_face.Info.MaxAdvanceWidth); + max_glyph_size.y = ImMax(max_glyph_size.y, font_face.Info.Ascender - font_face.Info.Descender); + + if (!cfg.GlyphRanges) + 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. + atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; + + // We don't do the original first pass to determine texture height, but just rough estimate. + // Looks ugly inaccurate and excessive, but AFAIK with FreeType we actually need to render glyphs to get exact sizes. + // Alternatively, we could just render all glyphs into a big shadow buffer, get their sizes, do the rectangle packing and just copy back from the + // shadow buffer to the texture buffer. Will give us an accurate texture height, but eat a lot of temp memory. Probably no one will notice.) + const int total_rects = total_glyphs_count + atlas->CustomRects.size(); + float min_rects_per_row = ceilf((atlas->TexWidth / (max_glyph_size.x + 1.0f))); + float min_rects_per_column = ceilf(total_rects / min_rects_per_row); + atlas->TexHeight = (int)(min_rects_per_column * (max_glyph_size.y + 1.0f)); + + // Create texture + atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight); + atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); + atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); + memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); + + // Start packing + ImVector pack_nodes; + pack_nodes.resize(total_rects); + stbrp_context context; + stbrp_init_target(&context, atlas->TexWidth, atlas->TexHeight, pack_nodes.Data, total_rects); + + // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + ImFontAtlasBuildPackCustomRects(atlas, &context); + + // Render characters, setup ImFont and glyphs for runtime + for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + { + ImFontConfig& cfg = atlas->ConfigData[input_i]; + FreeTypeFont& font_face = fonts[input_i]; + ImFont* dst_font = cfg.DstFont; + + const float ascent = font_face.Info.Ascender; + const float descent = font_face.Info.Descender; + ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); + const float off_x = cfg.GlyphOffset.x; + const float off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); + + bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f); + unsigned char multiply_table[256]; + if (multiply_enabled) + ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); + + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + for (uint32_t codepoint = in_range[0]; codepoint <= in_range[1]; ++codepoint) + { + if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint)) + continue; + + FT_Glyph ft_glyph = NULL; + FT_BitmapGlyph ft_glyph_bitmap = NULL; // NB: will point to bitmap within FT_Glyph + GlyphInfo glyph_info; + if (!font_face.CalcGlyphInfo(codepoint, glyph_info, ft_glyph, ft_glyph_bitmap)) + continue; + + // Pack rectangle + stbrp_rect rect; + rect.w = (uint16_t)glyph_info.Width + 1; // Account for texture filtering + rect.h = (uint16_t)glyph_info.Height + 1; + stbrp_pack_rects(&context, &rect, 1); + + // Copy rasterized pixels to main texture + uint8_t* blit_dst = atlas->TexPixelsAlpha8 + rect.y * atlas->TexWidth + rect.x; + font_face.BlitGlyph(ft_glyph_bitmap, blit_dst, atlas->TexWidth, multiply_enabled ? multiply_table : NULL); + FT_Done_Glyph(ft_glyph); + + // Register glyph + dst_font->AddGlyph((ImWchar)codepoint, + glyph_info.OffsetX + off_x, + glyph_info.OffsetY + off_y, + glyph_info.OffsetX + off_x + glyph_info.Width, + glyph_info.OffsetY + off_y + glyph_info.Height, + rect.x / (float)atlas->TexWidth, + rect.y / (float)atlas->TexHeight, + (rect.x + glyph_info.Width) / (float)atlas->TexWidth, + (rect.y + glyph_info.Height) / (float)atlas->TexHeight, + glyph_info.AdvanceX); + } + } + } + + // Cleanup + for (int n = 0; n < fonts.Size; n++) + fonts[n].Shutdown(); + + ImFontAtlasBuildFinish(atlas); + + return true; +} diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h new file mode 100644 index 00000000..f82dbc3b --- /dev/null +++ b/misc/freetype/imgui_freetype.h @@ -0,0 +1,31 @@ +// Wrapper to use Freetype (instead of stb_truetype) for Dear ImGui +// Original code by @Vuhdo +// Get latest version at http://www.github.com/ocornut/imgui_club + +#pragma once + +#include "imgui.h" // IMGUI_API, ImFontAtlas + +namespace ImGuiFreeType +{ + // Hinting greatly impacts visuals (and glyph sizes). + // When disabled, FreeType generates blurrier glyphs, more or less matches the stb's output. + // The Default hinting mode usually looks good, but may distort glyphs in an unusual way. + // The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. + + // You can set those flags on a per font basis in ImFontConfig::RasterizerFlags. + // Use the 'extra_flags' parameter of BuildFontAtlas() to force a flag on all your fonts. + enum RasterizerFlags + { + // By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter. + NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes. + NoAutoHint = 1 << 1, // Disable auto-hinter. + ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter. + LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. + MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. + Bold = 1 << 5, // Styling: Should we artificially embolden the font? + Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? + }; + + IMGUI_API bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags = 0); +} From 7b005bd7dec7f75e7a786da4ba35378565d547b9 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 4 Feb 2018 12:44:31 +0100 Subject: [PATCH 09/22] imgui_freetype: Documentation, tweaks. (#618) --- misc/fonts/README.txt | 4 +- misc/freetype/README.md | 126 +++++++++++++++++++++++++++++++ misc/freetype/imgui_freetype.cpp | 11 +-- misc/freetype/imgui_freetype.h | 14 ++-- 4 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 misc/freetype/README.md diff --git a/misc/fonts/README.txt b/misc/fonts/README.txt index 2287f6ea..a3774ca7 100644 --- a/misc/fonts/README.txt +++ b/misc/fonts/README.txt @@ -120,9 +120,7 @@ In this document: Dear Imgui uses stb_truetype.h to rasterize fonts (with optional oversampling). This technique and implementation are not ideal for fonts rendered at _small sizes_, which may appear a little blurry. - There is an implementation of the ImFontAtlas builder using FreeType that you can use: - - https://github.com/ocornut/imgui_club + There is an implementation of the ImFontAtlas builder using FreeType that you can use in the misc/freetype/ folder. FreeType supports auto-hinting which tends to improve the readability of small fonts. Note that this code currently creates textures that are unoptimally too large (could be fixed with some work) diff --git a/misc/freetype/README.md b/misc/freetype/README.md new file mode 100644 index 00000000..8b5d01d4 --- /dev/null +++ b/misc/freetype/README.md @@ -0,0 +1,126 @@ +# imgui_freetype + +This is an attempt to replace stb_truetype (the default imgui's font rasterizer) with FreeType. +Currently not optimal and probably has some limitations or bugs. +By [Vuhdo](https://github.com/Vuhdo) (Aleksei Skriabin). Improvements by @mikesart. Maintained by @ocornut. + +**Usage** +1. Get latest FreeType binaries or build yourself. +2. Add imgui_freetype.h/cpp alongside your imgui sources. +3. Include imgui_freetype.h after imgui.h. +4. Call ImGuiFreeType::BuildFontAtlas() *BEFORE* calling ImFontAtlas::GetTexDataAsRGBA32() or ImFontAtlas::Build() (so normal Build() won't be called): + +```cpp +// See ImGuiFreeType::RasterizationFlags +unsigned int flags = ImGuiFreeType::DisableHinting; +ImGuiFreeType::BuildFontAtlas(io.Fonts, flags); +io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); +``` + +**Test code Usage** +```cpp +#include "misc/freetype/imgui_freetype.h" +#include "misc/freetype/imgui_freetype.cpp" + +// Load various small fonts +ImGuiIO& io = ImGui::GetIO(); +io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 13.0f); +io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 13.0f); +io.Fonts->AddFontDefault(); + +FreeTypeTest freetype_test; + +// Main Loop +while (true) +{ + if (freetype_test.UpdateRebuild()) + { + // REUPLOAD FONT TEXTURE TO GPU + // e.g ImGui_ImplGlfwGL3_InvalidateDeviceObjects() + ImGui_ImplGlfwGL3_CreateDeviceObjects() + } + ImGui::NewFrame(); + freetype_test.ShowFreetypeOptionsWindow(); + ... + } +} +``` + +**Test code** +```cpp +#include "misc/freetype/imgui_freetype.h" +#include "misc/freetype/imgui_freetype.cpp" + +struct FreeTypeTest +{ + enum FontBuildMode + { + FontBuildMode_FreeType, + FontBuildMode_Stb, + }; + + FontBuildMode BuildMode; + bool WantRebuild; + float FontsMultiply; + unsigned int FontsFlags; + + FreeTypeTest() + { + BuildMode = FontBuildMode_FreeType; + WantRebuild = true; + FontsMultiply = 1.0f; + FontsFlags = 0; + } + + // Call _BEFORE_ NewFrame() + bool UpdateRebuild() + { + if (!WantRebuild) + return false; + ImGuiIO& io = ImGui::GetIO(); + for (int n = 0; n < io.Fonts->Fonts.Size; n++) + { + io.Fonts->Fonts[n]->ConfigData->RasterizerMultiply = FontsMultiply; + io.Fonts->Fonts[n]->ConfigData->RasterizerFlags = (BuildMode == FontBuildMode_FreeType) ? FontsFlags : 0x00; + } + if (BuildMode == FontBuildMode_FreeType) + ImGuiFreeType::BuildFontAtlas(io.Fonts, FontsFlags); + else if (BuildMode == FontBuildMode_Stb) + io.Fonts->Build(); + WantRebuild = false; + return true; + } + + // Call to draw interface + void ShowFreetypeOptionsWindow() + { + ImGui::Begin("FreeType Options"); + ImGui::ShowFontSelector("Fonts"); + WantRebuild |= ImGui::RadioButton("FreeType", (int*)&BuildMode, FontBuildMode_FreeType); + ImGui::SameLine(); + WantRebuild |= ImGui::RadioButton("Stb (Default)", (int*)&BuildMode, FontBuildMode_Stb); + WantRebuild |= ImGui::DragFloat("Multiply", &FontsMultiply, 0.001f, 0.0f, 2.0f); + if (BuildMode == FontBuildMode_FreeType) + { + WantRebuild |= ImGui::CheckboxFlags("NoHinting", &FontsFlags, ImGuiFreeType::NoHinting); + WantRebuild |= ImGui::CheckboxFlags("NoAutoHint", &FontsFlags, ImGuiFreeType::NoAutoHint); + WantRebuild |= ImGui::CheckboxFlags("ForceAutoHint", &FontsFlags, ImGuiFreeType::ForceAutoHint); + WantRebuild |= ImGui::CheckboxFlags("LightHinting", &FontsFlags, ImGuiFreeType::LightHinting); + WantRebuild |= ImGui::CheckboxFlags("MonoHinting", &FontsFlags, ImGuiFreeType::MonoHinting); + WantRebuild |= ImGui::CheckboxFlags("Bold", &FontsFlags, ImGuiFreeType::Bold); + WantRebuild |= ImGui::CheckboxFlags("Oblique", &FontsFlags, ImGuiFreeType::Oblique); + } + ImGui::End(); + } +}; +``` + +**Known issues** +- Output texture has excessive resolution (lots of vertical waste) +- FreeType's memory allocator is not overridden. + +**Obligatory comparison screenshots** + +Using Windows built-in segoeui.ttf font. Open in new browser tabs, view at 1080p+. + +![freetype rasterizer](https://raw.githubusercontent.com/wiki/ocornut/imgui_club/images/freetype_20170817.png) + diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 968d7f17..9a04ed89 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -1,16 +1,17 @@ // Wrapper to use Freetype (instead of stb_truetype) for Dear ImGui -// Get latest version at http://www.github.com/ocornut/imgui_club -// Original code by @Vuhdo (Aleksei Skriabin) +// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype +// Original code by @Vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained by @ocornut // Changelog: -// - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype, updated for latest changes in ImFontAtlas, minor tweaks. +// - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks. // - v0.51: (2017/08/26) cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. // - v0.52: (2017/09/26) fixes for imgui internal changes // - v0.53: (2017/10/22) minor inconsequential change to match change in master (removed an unnecessary statement) // - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member +// - v0.55: (2018/02/04) moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) -// Todo/Bugs: -// - Font size has lots of waste. +// TODO: +// - Output texture has excessive resolution (lots of vertical waste) // - FreeType's memory allocator is not overridden. #include "imgui_freetype.h" diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h index f82dbc3b..b8062bf7 100644 --- a/misc/freetype/imgui_freetype.h +++ b/misc/freetype/imgui_freetype.h @@ -1,6 +1,6 @@ // Wrapper to use Freetype (instead of stb_truetype) for Dear ImGui -// Original code by @Vuhdo -// Get latest version at http://www.github.com/ocornut/imgui_club +// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype +// Original code by @Vuhdo (Aleksei Skriabin), maintained by @ocornut #pragma once @@ -8,10 +8,10 @@ namespace ImGuiFreeType { - // Hinting greatly impacts visuals (and glyph sizes). - // When disabled, FreeType generates blurrier glyphs, more or less matches the stb's output. - // The Default hinting mode usually looks good, but may distort glyphs in an unusual way. - // The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. + // Hinting greatly impacts visuals (and glyph sizes). + // When disabled, FreeType generates blurrier glyphs, more or less matches the stb's output. + // The Default hinting mode usually looks good, but may distort glyphs in an unusual way. + // The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. // You can set those flags on a per font basis in ImFontConfig::RasterizerFlags. // Use the 'extra_flags' parameter of BuildFontAtlas() to force a flag on all your fonts. @@ -24,7 +24,7 @@ namespace ImGuiFreeType LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. Bold = 1 << 5, // Styling: Should we artificially embolden the font? - Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? + Oblique = 1 << 6 // Styling: Should we slant the font, emulating italic style? }; IMGUI_API bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags = 0); From 4786321754cf2c69dc6a0a1e803d18a726ce05ef Mon Sep 17 00:00:00 2001 From: Gerry Hernandez Date: Sun, 4 Feb 2018 21:47:08 -0500 Subject: [PATCH 10/22] Fix iOS code; fixes entire XCode build for Apple example --- .gitignore | 1 + examples/apple_example/imguiex-ios/debug_hud.cpp | 9 +++++++-- examples/apple_example/imguiex-ios/debug_hud.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/examples/apple_example/imguiex-ios/debug_hud.cpp b/examples/apple_example/imguiex-ios/debug_hud.cpp index 002d6bac..28722b56 100644 --- a/examples/apple_example/imguiex-ios/debug_hud.cpp +++ b/examples/apple_example/imguiex-ios/debug_hud.cpp @@ -22,6 +22,11 @@ void DebugHUD_InitDefaults( DebugHUD *hud ) hud->cubeColor2[1] = 0.4f; hud->cubeColor2[2] = 0.4f; hud->cubeColor2[3] = 1.0f; + + hud->clearColor[0] = 0.45f; + hud->clearColor[1] = 0.55f; + hud->clearColor[2] = 0.60f; + hud->clearColor[3] = 1.00f; } void DebugHUD_DoInterface(DebugHUD *hud) @@ -33,7 +38,7 @@ void DebugHUD_DoInterface(DebugHUD *hud) static int counter = 0; ImGui::Text("Hello, world!"); // Display some text (you can use a format string too) ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + ImGui::ColorEdit3("clear color", hud->clearColor); // Edit 3 floats representing a color ImGui::Checkbox("Demo Window", &hud->show_demo_window); // Edit bools storing our windows open/close state ImGui::Checkbox("Another Window", &hud->show_another_window); @@ -49,7 +54,7 @@ void DebugHUD_DoInterface(DebugHUD *hud) // 2. Show another simple window. In most cases you will use an explicit Begin/End pair to name your windows. if (hud->show_another_window) { - ImGui::Begin("Another Window", &hud-?show_another_window); + ImGui::Begin("Another Window", &hud->show_another_window); ImGui::Text("Hello from another window!"); ImGui::ColorEdit3("Cube 1 Color", hud->cubeColor1); ImGui::ColorEdit3("Cube 2 Color", hud->cubeColor2); diff --git a/examples/apple_example/imguiex-ios/debug_hud.h b/examples/apple_example/imguiex-ios/debug_hud.h index 89b79a41..4ef53572 100644 --- a/examples/apple_example/imguiex-ios/debug_hud.h +++ b/examples/apple_example/imguiex-ios/debug_hud.h @@ -11,6 +11,7 @@ typedef struct DebugHUD float rotation_speed; float cubeColor1[4]; float cubeColor2[4]; + float clearColor[4]; } DebugHUD; #if __cplusplus From a33b86dac7bd44826ca02616c9573dd330d2764f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 09:14:33 +0100 Subject: [PATCH 11/22] Removed root .gitignore. (#1594) --- .gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 5761abcf..00000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o From cf6b39600bfbceb0c404cb8607b49f30a8fdc4e6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 11:21:06 +0100 Subject: [PATCH 12/22] imgui_freetype: comments about correct blending and sRGB (#618, #578) --- misc/freetype/README.md | 6 ++++++ misc/freetype/imgui_freetype.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/misc/freetype/README.md b/misc/freetype/README.md index 8b5d01d4..ad0e4d3b 100644 --- a/misc/freetype/README.md +++ b/misc/freetype/README.md @@ -17,6 +17,12 @@ ImGuiFreeType::BuildFontAtlas(io.Fonts, flags); io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); ``` +**Gamma Correct Blending** +FreeType assumes blending in linear space rather than gamma space. +See FreeType note for [FT_Render_Glyph](https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph). +For correct results you need to be using sRGB and convert to linear space in the pixel shader output. +The default imgui styles will be impacted by this change (alpha values will need tweaking). + **Test code Usage** ```cpp #include "misc/freetype/imgui_freetype.h" diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 9a04ed89..9e373c7d 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -10,6 +10,12 @@ // - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member // - v0.55: (2018/02/04) moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) +// Gamma Correct Blending: +// FreeType assumes blending in linear space rather than gamma space. +// See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph +// For correct results you need to be using sRGB and convert to linear space in the pixel shader output. +// The default imgui styles will be impacted by this change (alpha values will need tweaking). + // TODO: // - Output texture has excessive resolution (lots of vertical waste) // - FreeType's memory allocator is not overridden. From bed55a41e1e5ef2242d2f1a3a1d93254ffd1953b Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 14:51:28 +0100 Subject: [PATCH 13/22] Internals: ImRect: Added IsInverted() helper. --- imgui_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_internal.h b/imgui_internal.h index 37a31bff..f904cbcf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -285,6 +285,7 @@ struct IMGUI_API ImRect void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } void FixInverted() { if (Min.x > Max.x) ImSwap(Min.x, Max.x); if (Min.y > Max.y) ImSwap(Min.y, Max.y); } + bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } bool IsFinite() const { return Min.x != FLT_MAX; } }; From 3f297e74e4f25c6307a49a1c50348708c7a332f4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 14:52:28 +0100 Subject: [PATCH 14/22] Merging the minor/shallow changes from Navigation branch. --- imgui.cpp | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9dd9eb77..53658069 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -23,7 +23,7 @@ - ISSUES & TODO LIST - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - How can I help? - - How can I dipslay an image? What is ImTextureID, how does it works? + - How can I display an image? What is ImTextureID, how does it works? - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels and the ID stack. - How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application? - How can I load a different font than the default? @@ -2135,9 +2135,8 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) return true; - if (allow_keyboard_focus) - if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) - return true; + if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + return true; return false; } @@ -3831,10 +3830,8 @@ static ImGuiWindow* GetFrontMostModalRootWindow() static void ClosePopupToLevel(int remaining) { ImGuiContext& g = *GImGui; - if (remaining > 0) - ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window); - else - ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow); + ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; + ImGui::FocusWindow(focus_window); g.OpenPopupStack.resize(remaining); } @@ -4029,12 +4026,14 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) { + IM_ASSERT(id != 0); return BeginChildEx(NULL, id, size_arg, border, extra_flags); } void ImGui::EndChild() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss if (window->BeginCount > 1) @@ -4051,7 +4050,7 @@ void ImGui::EndChild() sz.y = ImMax(4.0f, sz.y); End(); - ImGuiWindow* parent_window = GetCurrentWindow(); + ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); ItemAdd(bb, 0); @@ -4473,6 +4472,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Automatically disable manual moving/resizing when NoInputs is set if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; + //if (flags & ImGuiWindowFlags_NavFlattened) // IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); @@ -4578,9 +4578,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->RootWindow = parent_window->RootWindow; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootNonPopupWindow = parent_window->RootNonPopupWindow; - //window->RootNavWindow = window; - //while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened) - // window->RootNavWindow = window->RootNavWindow->ParentWindow; + //window->NavRootWindow = window; + //while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened) + // window->NavRootWindow = window->NavRootWindow->ParentWindow; window->Active = true; window->BeginOrderWithinParent = 0; @@ -4796,12 +4796,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const float window_border_size = window->WindowBorderSize; ImRect title_bar_rect = window->TitleBarRect(); const bool window_is_focused = want_focus || (g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow); + ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); if (window->Collapsed) { // Title bar only float backup_border_size = style.FrameBorderSize; g.Style.FrameBorderSize = window->WindowBorderSize; - RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding); + RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); g.Style.FrameBorderSize = backup_border_size; } else @@ -4817,7 +4818,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImDrawCornerFlags_Top); + window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); // Menu bar if (flags & ImGuiWindowFlags_MenuBar) @@ -6449,7 +6450,7 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) return pressed; } -// Upper-right button to close a window. +// Button to close a window bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) { ImGuiWindow* window = GetCurrentWindow(); @@ -6471,7 +6472,6 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); } - return pressed; } @@ -7647,14 +7647,11 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s float adjust_delta = 0.0f; if (IsMousePosValid()) { - //if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; - if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) - adjust_delta *= g.DragSpeedScaleFast; - if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) - adjust_delta *= g.DragSpeedScaleSlow; - } + adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + adjust_delta *= g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + adjust_delta *= g.DragSpeedScaleSlow; g.DragLastMouseDelta.x = mouse_drag_delta.x; } adjust_delta *= v_speed; @@ -9786,7 +9783,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) g.NavWindow = backed_nav_window; bool want_open = false, want_close = false; - if (window->DC.LayoutType != ImGuiLayoutType_Horizontal) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) { // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_within_opened_triangle = false; @@ -11771,8 +11768,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) { if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) return; + ImGuiWindowFlags flags = window->Flags; NodeDrawList(window, window->DrawList, "DrawList"); ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); + ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags, + (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", + (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : ""); ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); From 1dedbf80916968528028dba83e2aa5f328690815 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 15:07:25 +0100 Subject: [PATCH 15/22] Style: Fixed missing style.MouseCursorScale initialization (#939), messed up original commit badly. --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 53658069..d047b4b0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -745,6 +745,7 @@ ImGuiStyle::ImGuiStyle() ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. From 11d0fc8220675e88e117f509e888ed6b7e8c97c6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 15:10:07 +0100 Subject: [PATCH 16/22] Tooltip: BeginTooltip() sets NoInputs flag. --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d047b4b0..20ee693e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3708,7 +3708,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_ window->HiddenFrames = 1; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; Begin(window_name, NULL, flags | extra_flags); } @@ -11384,7 +11384,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button) //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :( SetNextWindowPos(g.IO.MousePos); PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f)); - BeginTooltipEx(ImGuiWindowFlags_NoInputs); + BeginTooltip(); } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) From 60fb7d31fde0dad94c48e5a2839acf09a10c5cf0 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 22:35:29 +0100 Subject: [PATCH 17/22] Examples: SDL2: Enabling vsync. Using scancode consistently, fixed keyboard weirdness. --- .../imgui_impl_sdl_gl2.cpp | 26 ++++++++++--------- examples/sdl_opengl2_example/main.cpp | 1 + .../imgui_impl_sdl_gl3.cpp | 26 ++++++++++--------- examples/sdl_opengl3_example/main.cpp | 1 + 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp index 801c433f..a641ec19 100644 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp +++ b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp @@ -152,7 +152,8 @@ bool ImGui_ImplSdlGL2_ProcessEvent(SDL_Event* event) case SDL_KEYDOWN: case SDL_KEYUP: { - int key = event->key.keysym.sym & ~SDLK_SCANCODE_MASK; + int key = event->key.keysym.scancode; + IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)); io.KeysDown[key] = (event->type == SDL_KEYDOWN); io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); @@ -203,8 +204,9 @@ void ImGui_ImplSdlGL2_InvalidateDeviceObjects() bool ImGui_ImplSdlGL2_Init(SDL_Window* window) { + // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = SDLK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. + io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; @@ -214,16 +216,16 @@ bool ImGui_ImplSdlGL2_Init(SDL_Window* window) io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = SDLK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = SDLK_BACKSPACE; - io.KeyMap[ImGuiKey_Enter] = SDLK_RETURN; - io.KeyMap[ImGuiKey_Escape] = SDLK_ESCAPE; - io.KeyMap[ImGuiKey_A] = SDLK_a; - io.KeyMap[ImGuiKey_C] = SDLK_c; - io.KeyMap[ImGuiKey_V] = SDLK_v; - io.KeyMap[ImGuiKey_X] = SDLK_x; - io.KeyMap[ImGuiKey_Y] = SDLK_y; - io.KeyMap[ImGuiKey_Z] = SDLK_z; + io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; + io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; + io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; + io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; + io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; + io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; + io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; + io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; + io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; + io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; io.RenderDrawListsFn = ImGui_ImplSdlGL2_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. io.SetClipboardTextFn = ImGui_ImplSdlGL2_SetClipboardText; diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 10e66e80..becf841a 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -31,6 +31,7 @@ int main(int, char**) SDL_GetCurrentDisplayMode(0, ¤t); SDL_Window *window = SDL_CreateWindow("ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); SDL_GLContext glcontext = SDL_GL_CreateContext(window); + SDL_GL_SetSwapInterval(1); // Enable vsync // Setup ImGui binding ImGui_ImplSdlGL2_Init(window); diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 35d9cdde..8368e4af 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -174,7 +174,8 @@ bool ImGui_ImplSdlGL3_ProcessEvent(SDL_Event* event) case SDL_KEYDOWN: case SDL_KEYUP: { - int key = event->key.keysym.sym & ~SDLK_SCANCODE_MASK; + int key = event->key.keysym.scancode; + IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)); io.KeysDown[key] = (event->type == SDL_KEYDOWN); io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); @@ -314,8 +315,9 @@ void ImGui_ImplSdlGL3_InvalidateDeviceObjects() bool ImGui_ImplSdlGL3_Init(SDL_Window* window) { + // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = SDLK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. + io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; @@ -325,16 +327,16 @@ bool ImGui_ImplSdlGL3_Init(SDL_Window* window) io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = SDLK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = SDLK_BACKSPACE; - io.KeyMap[ImGuiKey_Enter] = SDLK_RETURN; - io.KeyMap[ImGuiKey_Escape] = SDLK_ESCAPE; - io.KeyMap[ImGuiKey_A] = SDLK_a; - io.KeyMap[ImGuiKey_C] = SDLK_c; - io.KeyMap[ImGuiKey_V] = SDLK_v; - io.KeyMap[ImGuiKey_X] = SDLK_x; - io.KeyMap[ImGuiKey_Y] = SDLK_y; - io.KeyMap[ImGuiKey_Z] = SDLK_z; + io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; + io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; + io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; + io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; + io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; + io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; + io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; + io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; + io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; + io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; io.RenderDrawListsFn = ImGui_ImplSdlGL3_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. io.SetClipboardTextFn = ImGui_ImplSdlGL3_SetClipboardText; diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 2d7e8597..4bab4916 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -30,6 +30,7 @@ int main(int, char**) SDL_GetCurrentDisplayMode(0, ¤t); SDL_Window *window = SDL_CreateWindow("ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); SDL_GLContext glcontext = SDL_GL_CreateContext(window); + SDL_GL_SetSwapInterval(1); // Enable vsync gl3wInit(); // Setup ImGui binding From fcf1c9d151c5a53ded2982ba3dc7109da071bcb4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 23:09:14 +0100 Subject: [PATCH 18/22] Examples: SDL: Using SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency() to handle framerate over 1000 fps properly. Noticed bad inputs artefacts in Nav branch at 2000 fps without this. (#996) --- examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp | 10 +++++----- examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp index a641ec19..4cbe0030 100644 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp +++ b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp @@ -22,7 +22,7 @@ #include "imgui_impl_sdl_gl2.h" // Data -static double g_Time = 0.0f; +static Uint64 g_Time = 0; static bool g_MousePressed[3] = { false, false, false }; static GLuint g_FontTexture = 0; @@ -265,10 +265,10 @@ void ImGui_ImplSdlGL2_NewFrame(SDL_Window *window) io.DisplaySize = ImVec2((float)w, (float)h); io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - // Setup time step - Uint32 time = SDL_GetTicks(); - double current_time = time / 1000.0; - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + static Uint64 frequency = SDL_GetPerformanceFrequency(); + Uint64 current_time = SDL_GetPerformanceCounter(); + io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); g_Time = current_time; // Setup mouse inputs (we already got mouse wheel, keyboard keys & characters from our event handler) diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 8368e4af..6cec9152 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -17,7 +17,7 @@ #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. // Data -static double g_Time = 0.0f; +static Uint64 g_Time = 0.0f; static bool g_MousePressed[3] = { false, false, false }; static GLuint g_FontTexture = 0; static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; @@ -376,10 +376,10 @@ void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window) io.DisplaySize = ImVec2((float)w, (float)h); io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - // Setup time step - Uint32 time = SDL_GetTicks(); - double current_time = time / 1000.0; - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + static Uint64 frequency = SDL_GetPerformanceFrequency(); + Uint64 current_time = SDL_GetPerformanceCounter(); + io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); g_Time = current_time; // Setup mouse inputs (we already got mouse wheel, keyboard keys & characters from our event handler) From e6926d9f7c6febb64e9c8458fb8e1c0de321d576 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:34:41 +0100 Subject: [PATCH 19/22] Internals: Refactored bool LastItemRectHovered into flags. (#1597) --- imgui.cpp | 17 +++++++++-------- imgui_internal.h | 24 ++++++++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 20ee693e..6b107afd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2035,13 +2035,14 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id) const bool is_clipped = IsClippedEx(bb, id, false); window->DC.LastItemId = id; window->DC.LastItemRect = bb; - window->DC.LastItemRectHoveredRect = false; + window->DC.LastItemStatusFlags = 0; if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) - window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(bb.Min, bb.Max); + if (IsMouseHoveringRect(bb.Min, bb.Max)) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } @@ -2054,7 +2055,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiWindow* window = g.CurrentWindow; // Test for bounding box overlap, as updated as ItemAdd() - if (!window->DC.LastItemRectHoveredRect) + if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function @@ -4970,8 +4971,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.). window->DC.LastItemId = window->MoveId; + window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; - window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false); } // Inner clipping rectangle @@ -10283,7 +10284,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag EndGroup(); // Drag and Drop Target - if (window->DC.LastItemRectHoveredRect && BeginDragDropTarget()) // NB: The LastItemRectHoveredRect test is merely an optional micro-optimization + if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && BeginDragDropTarget()) // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. { if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) { @@ -11337,7 +11338,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button) // We build a throwaway ID based on current ID stack + relative AABB of items in window. // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - bool is_hovered = window->DC.LastItemRectHoveredRect; + bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0; if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); @@ -11388,7 +11389,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button) } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - window->DC.LastItemRectHoveredRect = false; + window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; return true; } @@ -11485,7 +11486,7 @@ bool ImGui::BeginDragDropTarget() return false; ImGuiWindow* window = g.CurrentWindow; - if (!window->DC.LastItemRectHoveredRect) + if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) return false; diff --git a/imgui_internal.h b/imgui_internal.h index f904cbcf..e80c5297 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -45,6 +45,7 @@ struct ImGuiWindowSettings; typedef int ImGuiLayoutType; // enum: horizontal or vertical // enum ImGuiLayoutType_ typedef int ImGuiButtonFlags; // flags: for ButtonEx(), ButtonBehavior() // enum ImGuiButtonFlags_ typedef int ImGuiItemFlags; // flags: for PushItemFlag() // enum ImGuiItemFlags_ +typedef int ImGuiItemStatusFlags; // flags: storage for DC.LastItemXXX // enum ImGuiItemStatusFlags_ typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ @@ -216,6 +217,12 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_Vertical = 1 << 1 }; +// Storage for LastItem data +enum ImGuiItemStatusFlags_ +{ + ImGuiItemStatusFlags_HoveredRect = 1 << 0 +}; + // FIXME: this is in development, not exposed/functional as a generic feature yet. enum ImGuiLayoutType_ { @@ -673,6 +680,7 @@ struct ImGuiContext }; // Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). +// This is going to be exposed in imgui.h when stabilized enough. enum ImGuiItemFlags_ { ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true @@ -699,8 +707,8 @@ struct IMGUI_API ImGuiDrawContext float LogLinePosY; int TreeDepth; ImGuiID LastItemId; - ImRect LastItemRect; - bool LastItemRectHoveredRect; + ImGuiItemStatusFlags LastItemStatusFlags; + ImRect LastItemRect; // Interaction rect bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -730,8 +738,8 @@ struct IMGUI_API ImGuiDrawContext LogLinePosY = -1.0f; TreeDepth = 0; LastItemId = 0; + LastItemStatusFlags = 0; LastItemRect = ImRect(); - LastItemRectHoveredRect = false; MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; @@ -838,13 +846,13 @@ public: // Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. struct ImGuiItemHoveredDataBackup { - ImGuiID LastItemId; - ImRect LastItemRect; - bool LastItemRectHoveredRect; + ImGuiID LastItemId; + ImGuiItemStatusFlags LastItemFlags; + ImRect LastItemRect; ImGuiItemHoveredDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemRect = window->DC.LastItemRect; LastItemRectHoveredRect = window->DC.LastItemRectHoveredRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemRect = LastItemRect; window->DC.LastItemRectHoveredRect = LastItemRectHoveredRect; } + void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; } + void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemFlags; window->DC.LastItemRect = LastItemRect; } }; //----------------------------------------------------------------------------- From c6ba7c8231e20ea225ba466400f254b01aca7832 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:41:46 +0100 Subject: [PATCH 20/22] Internals: TreeNode: Renaming local to be more consistent with other widgets. --- imgui.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6b107afd..a2db61cc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6741,12 +6741,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // We vertically grow up to current line height up the typical widget height. const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); - ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); + ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); if (display_frame) { // Framed header expand a little outside the default padding - bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; - bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; + frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; + frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; } const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing @@ -6755,7 +6755,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) - const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y); + const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); if (!ItemAdd(interact_bb, id)) { @@ -6796,36 +6796,36 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, text_base_offset_y); + const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); if (display_frame) { // Framed type - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - RenderTriangle(bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); + RenderTriangle(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); if (g.LogEnabled) { // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. const char log_prefix[] = "\n##"; const char log_suffix[] = "##"; LogRenderedText(&text_pos, log_prefix, log_prefix+3); - RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); } else { - RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); } } else { // Unframed typed for tree nodes if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) - RenderFrame(bb.Min, bb.Max, col, false); + RenderFrame(frame_bb.Min, frame_bb.Max, col, false); if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); + RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); else if (!(flags & ImGuiTreeNodeFlags_Leaf)) - RenderTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); + RenderTriangle(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); if (g.LogEnabled) LogRenderedText(&text_pos, ">"); RenderText(text_pos, label, label_end, false); From b50dce54ea0afb7ccc3be479abaeb96976bcdc69 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:46:14 +0100 Subject: [PATCH 21/22] Drag and Drop: TreeNode as drop target displays rectangle over full frame. Added optional internal storage for item display rect. Will expose later. (#1597, #143) --- imgui.cpp | 17 +++++++++++------ imgui_internal.h | 13 ++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a2db61cc..4312ed18 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6757,7 +6757,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); - if (!ItemAdd(interact_bb, id)) + bool item_add = ItemAdd(interact_bb, id); + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + window->DC.LastItemDisplayRect = frame_bb; + + if (!item_add) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushRawID(id); @@ -9537,7 +9541,7 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - window->DC.LastItemRect = bb; + window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. BeginGroup(); if (label_size.x > 0) @@ -10703,9 +10707,9 @@ bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float #ifdef IMGUI_HAS_NAV window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; #endif - bool add = ItemAdd(bb, id); + bool item_add = ItemAdd(bb, id); window->DC.ItemFlags = item_flags_backup; - if (!add) + if (!item_add) return false; bool hovered, held; @@ -11491,13 +11495,14 @@ bool ImGui::BeginDragDropTarget() if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) return false; + const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; ImGuiID id = window->DC.LastItemId; if (id == 0) - id = window->GetIDFromRectangle(window->DC.LastItemRect); + id = window->GetIDFromRectangle(display_rect); if (g.DragDropPayload.SourceId == id) return false; - g.DragDropTargetRect = window->DC.LastItemRect; + g.DragDropTargetRect = display_rect; g.DragDropTargetId = id; return true; } diff --git a/imgui_internal.h b/imgui_internal.h index e80c5297..50046772 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -220,7 +220,8 @@ enum ImGuiSeparatorFlags_ // Storage for LastItem data enum ImGuiItemStatusFlags_ { - ImGuiItemStatusFlags_HoveredRect = 1 << 0 + ImGuiItemStatusFlags_HoveredRect = 1 << 0, + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1 }; // FIXME: this is in development, not exposed/functional as a generic feature yet. @@ -709,6 +710,7 @@ struct IMGUI_API ImGuiDrawContext ImGuiID LastItemId; ImGuiItemStatusFlags LastItemStatusFlags; ImRect LastItemRect; // Interaction rect + ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -739,7 +741,7 @@ struct IMGUI_API ImGuiDrawContext TreeDepth = 0; LastItemId = 0; LastItemStatusFlags = 0; - LastItemRect = ImRect(); + LastItemRect = LastItemDisplayRect = ImRect(); MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; @@ -847,12 +849,13 @@ public: struct ImGuiItemHoveredDataBackup { ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemFlags; + ImGuiItemStatusFlags LastItemStatusFlags; ImRect LastItemRect; + ImRect LastItemDisplayRect; ImGuiItemHoveredDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemFlags; window->DC.LastItemRect = LastItemRect; } + void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } + void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } }; //----------------------------------------------------------------------------- From 3b7bd4563a0b15f850bce3b0d5eefb39d8ad8006 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 16:32:51 +0100 Subject: [PATCH 22/22] ImVec2: added [] operator. This is becoming desirable for some types of code, better added sooner than later. --- imgui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.h b/imgui.h index 5c436c02..3efc5978 100644 --- a/imgui.h +++ b/imgui.h @@ -113,6 +113,7 @@ struct ImVec2 float x, y; ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } + float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return *(&x + idx); } // We very rarely use this [] operator, thus an assert is fine. #ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2. IM_VEC2_CLASS_EXTRA #endif