From 5f16edd0df8478c624824ee2f20f1d8dd11f96e2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 27 May 2024 17:31:20 -0500 Subject: [PATCH] cherry-pick ImGui code from LTVA1/furnace localization work --- extern/imgui_patched/imgui.cpp | 112 +++++- extern/imgui_patched/imgui.h | 11 +- extern/imgui_patched/imgui_draw.cpp | 49 +++ extern/imgui_patched/imgui_internal.h | 13 +- extern/imgui_patched/imgui_widgets.cpp | 471 ++++++++++++++++++++++--- extern/imgui_patched/imstb_textedit.h | 2 +- 6 files changed, 587 insertions(+), 71 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index de063df0d..14ad6e2dc 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -1046,7 +1046,7 @@ static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For u static void SetCurrentWindow(ImGuiWindow* window); static void FindHoveredWindow(); -static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); +static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags, const char* displayedName = NULL); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); @@ -1219,6 +1219,8 @@ ImGuiStyle::ImGuiStyle() 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. CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + DoFrameShadingForMultilineText = false; + // Behaviors HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. @@ -3341,6 +3343,17 @@ const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) return text_display_end; } +const char* FindRenderedTextEndNoHashHide(const char* text, const char* text_end) +{ + const char* text_display_end = text; + if (!text_end) + text_end = (const char*)-1; + + while (text_display_end < text_end && *text_display_end != '\0') + text_display_end++; + return text_display_end; +} + // Internal ImGui functions to render text // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) @@ -3363,7 +3376,14 @@ void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool if (text != text_display_end) { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + if (hide_text_after_hash) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + } + else + { + window->DrawList->AddTextNoHashHide(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + } if (g.LogEnabled) LogRenderedText(&pos, text, text_display_end); } @@ -3385,6 +3405,22 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end } } +void ImGui::RenderTextWrappedNoHashHide(ImVec2 pos, const char* text, const char* text_end, float wrap_width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + + if (text != text_end) + { + window->DrawList->AddTextNoHashHide(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); + if (g.LogEnabled) + LogRenderedText(&pos, text, text_end); + } +} + // Default clip_rect uses (pos_min,pos_max) // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList. @@ -3433,6 +3469,21 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons LogRenderedText(&pos_min, text, text_display_end); } +void ImGui::RenderTextClippedNoHashHide(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Do not hide anything after a '##' string + const char* text_display_end = text_end; + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); + if (g.LogEnabled) + LogRenderedText(&pos_min, text, text_display_end); +} + // Another overly complex function until we reorganize everything into a nice all-in-one helper. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display. // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. @@ -3843,12 +3894,23 @@ void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) //----------------------------------------------------------------------------- // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods -ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL) +ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name, const char* displayedName) : DrawListInst(NULL) { memset(this, 0, sizeof(*this)); Ctx = ctx; Name = ImStrdup(name); NameBufLen = (int)strlen(name) + 1; + + if(displayedName != NULL) + { + DisplayedName = ImStrdup(displayedName); + } + + else + { + DisplayedName = NULL; + } + ID = ImHashStr(name); IDStack.push_back(ID); ViewportAllowPlatformMonitorExtend = -1; @@ -3879,6 +3941,7 @@ ImGuiWindow::~ImGuiWindow() { IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); + IM_DELETE(DisplayedName); ColumnsStorage.clear_destruct(); } @@ -5731,6 +5794,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b if (!border) g.Style.ChildBorderSize = 0.0f; bool ret = Begin(temp_window_name, NULL, flags); + g.Style.ChildBorderSize = backup_border_size; ImGuiWindow* child_window = g.CurrentWindow; @@ -5930,12 +5994,12 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s } } -static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) +static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags, const char* displayedName) { // Create window the first time //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); ImGuiContext& g = *GImGui; - ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); + ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name, displayedName); window->Flags = flags; g.WindowsById.SetVoidPtr(window->ID, window); @@ -6651,7 +6715,7 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) // You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. -bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) +bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, const char* displayedName) { ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; @@ -6663,7 +6727,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImGuiWindow* window = FindWindowByName(name); const bool window_just_created = (window == NULL); if (window_just_created) - window = CreateNewWindow(name, flags); + window = CreateNewWindow(name, flags, displayedName); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) @@ -6869,6 +6933,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->NameBufLen = (int)buf_len; } + if(window->DisplayedName) + { + if(strcmp(displayedName, window->DisplayedName) != 0) + { + // WAHAHAHAHA + // you wrote free() instead of IM_DELETE() and broke portability HAHAHA + IM_DELETE(window->DisplayedName); + window->DisplayedName = ImStrdup(displayedName); + } + } + + if(window->DisplayedName == NULL && displayedName != NULL) + { + window->DisplayedName = ImStrdup(displayedName); + } + // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS // Update contents size from last frame for auto-fitting (or use explicit size) @@ -7420,7 +7500,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) - RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); + RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), displayedName == NULL ? name : displayedName, p_open); // Clear hit test shape every frame window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; @@ -14183,8 +14263,14 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count) { ImGuiContext& g = *GImGui; - for (int n = 0; n < count; n++) - g.LocalizationTable[entries[n].Key] = entries[n].Text; + + if (GImGui != NULL) + { + for (int n = 0; n < count; n++) + { + g.LocalizationTable[entries[n].Key] = entries[n].Text; + } + } } @@ -16891,7 +16977,7 @@ bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) return false; if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return false; - Begin(node->HostWindow->Name); + Begin(node->HostWindow->Name); //this is to be edited PushOverrideID(node->ID); bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node); IM_UNUSED(ret); @@ -17094,7 +17180,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so) bool tab_open = true; - TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); + TabItemEx(tab_bar, window->DisplayedName == NULL ? window->Name : window->DisplayedName, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) node->WantCloseTabId = window->TabId; if (tab_bar->VisibleTabId == window->TabId) @@ -17551,7 +17637,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max); TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs); - TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL); + TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->DisplayedName == NULL ? payload_window->Name : payload_window->DisplayedName, 0, 0, false, NULL, NULL); if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PopClipRect(); } diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 97a458696..b86c20b17 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -338,7 +338,7 @@ namespace ImGui // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] // - Note that the bottom of window stack always contains a window called "Debug". - IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); + IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0, const char* displayedName = NULL); IMGUI_API void End(); // Child Windows @@ -496,6 +496,7 @@ namespace ImGui // Widgets: Text IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // formatted text + IMGUI_API void TextNoHashHide(const char* fmt, ...) IM_FMTARGS(1); // formatted text IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(2); @@ -1080,6 +1081,8 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) + ImGuiInputTextFlags_WordWrapping = 1 << 21, //https://github.com/MladoniSzabi/QuestSystem/commit/2c1f8bba6ecaa53a642ea469d49f19e94adc0029 + // Obsolete names //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior }; @@ -1143,6 +1146,8 @@ enum ImGuiSelectableFlags_ ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one + ImGuiSelectableFlags_NoHashTextHide = 1 << 5, // do not hide text after `##` + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 #endif @@ -2020,6 +2025,8 @@ struct ImGuiStyle float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; + bool DoFrameShadingForMultilineText; // Toggle FrameShading for multiline text input field + // Behaviors // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO) float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. @@ -2808,6 +2815,8 @@ struct ImDrawList IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); + IMGUI_API void AddTextNoHashHide(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); + IMGUI_API void AddTextNoHashHide(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) diff --git a/extern/imgui_patched/imgui_draw.cpp b/extern/imgui_patched/imgui_draw.cpp index 16d89bc8b..85e62beed 100644 --- a/extern/imgui_patched/imgui_draw.cpp +++ b/extern/imgui_patched/imgui_draw.cpp @@ -1620,6 +1620,17 @@ void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const Im PathStroke(col, 0, thickness); } +const char* FindTextEnd(const char* text, const char* text_end) +{ + const char* text_display_end = text; + if (!text_end) + text_end = (const char*)-1; + + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + text_display_end++; + return text_display_end; +} + void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) { if ((col & IM_COL32_A_MASK) == 0) @@ -1630,6 +1641,8 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, if (text_begin == text_end) return; + text_end = FindTextEnd(text_begin, text_end); //hides everything after ## + // Pull default font/size from the shared ImDrawListSharedData instance if (font == NULL) font = _Data->Font; @@ -1654,6 +1667,42 @@ void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, c AddText(NULL, 0.0f, pos, col, text_begin, text_end); } +void ImDrawList::AddTextNoHashHide(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + if (text_end == NULL) + text_end = text_begin + strlen(text_begin); + if (text_begin == text_end) + return; + + //text_end = FindTextEnd(text_begin, text_end); //hides everything after ## + + // Pull default font/size from the shared ImDrawListSharedData instance + if (font == NULL) + font = _Data->Font; + if (font_size == 0.0f) + font_size = _Data->FontSize; + + IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + ImVec4 clip_rect = _CmdHeader.ClipRect; + if (cpu_fine_clip_rect) + { + clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); + clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y); + clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); + clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); + } + font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); +} + +void ImDrawList::AddTextNoHashHide(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) +{ + AddTextNoHashHide(NULL, 0.0f, pos, col, text_begin, text_end); +} + void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index a9272ab33..94e1a6cdb 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -941,6 +941,8 @@ enum ImGuiTextFlags_ { ImGuiTextFlags_None = 0, ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0, + + ImGuiTextFlags_HideID = 1 << 1, //hide things after ## }; enum ImGuiTooltipFlags_ @@ -1109,6 +1111,7 @@ struct IMGUI_API ImGuiInputTextState bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. + float WordWrapWidth; ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } @@ -1117,6 +1120,8 @@ struct IMGUI_API ImGuiInputTextState int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation + bool HasWordWrap() const { return WordWrapWidth > 0.0; } + // Cursor & Selection void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } @@ -2513,6 +2518,9 @@ struct IMGUI_API ImGuiWindow { ImGuiContext* Ctx; // Parent UI context (needs to be set explicitly by parent). char* Name; // Window name, owned by the window. + + char* DisplayedName; // Window name, displayed in docked mode. + ImGuiID ID; // == ImHashStr(Name) ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass() @@ -2638,7 +2646,7 @@ struct IMGUI_API ImGuiWindow ImRect DockTabItemRect; public: - ImGuiWindow(ImGuiContext* context, const char* name); + ImGuiWindow(ImGuiContext* context, const char* name, const char* displayedName = NULL); ~ImGuiWindow(); ImGuiID GetID(const char* str, const char* str_end = NULL); @@ -3445,7 +3453,9 @@ namespace ImGui // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); + IMGUI_API void RenderTextWrappedNoHashHide(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextClippedNoHashHide(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); @@ -3468,6 +3478,7 @@ namespace ImGui // Widgets IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); + IMGUI_API void TextExNoHashHide(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index ff90e35ad..ec3412a96 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -122,7 +122,9 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); // For InputTextEx() static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, const bool stop_on_new_line = false, const float wrap_width = 0.0f, const bool keep_trailing_blanks = false); +static ImVec2 FindCharPosition(const ImWchar* text_begin, const ImWchar* char_position, const ImWchar* text_end, const float wrap_width); +static const ImWchar* CalcWordWrapPositionW(const ImFont* font, float scale, const ImWchar* text, const ImWchar* text_end, float wrap_width); //------------------------------------------------------------------------- // [SECTION] Widgets: Text, etc. @@ -159,6 +161,8 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) if (text_end == NULL) text_end = text + strlen(text); // FIXME-OPT + text_end = FindRenderedTextEnd(text, text_end); //hides everything after ## + const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); const float wrap_pos_x = window->DC.TextWrapPos; const bool wrap_enabled = (wrap_pos_x >= 0.0f); @@ -251,6 +255,116 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) } } +void ImGui::TextExNoHashHide(const char* text, const char* text_end, ImGuiTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + // Accept null ranges + if (text == text_end) + text = text_end = ""; + + // Calculate length + const char* text_begin = text; + if (text_end == NULL) + text_end = text + strlen(text); // FIXME-OPT + + //text_end = FindRenderedTextEnd(text, text_end); //hides everything after ## + + const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + const float wrap_pos_x = window->DC.TextWrapPos; + const bool wrap_enabled = (wrap_pos_x >= 0.0f); + if (text_end - text <= 2000 || wrap_enabled) + { + // Common case + const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; + const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size, 0.0f); + if (!ItemAdd(bb, 0)) + return; + + // Render (we don't hide text after ## in this end-user function) + RenderTextWrappedNoHashHide(bb.Min, text_begin, text_end, wrap_width); + } + else + { + // Long text! + // Perform manual coarse clipping to optimize for long multi-line text + // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. + const char* line = text; + const float line_height = GetTextLineHeight(); + ImVec2 text_size(0, 0); + + // Lines to skip (can't skip when logging text) + ImVec2 pos = text_pos; + if (!g.LogEnabled) + { + int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) + { + int lines_skipped = 0; + while (line < text_end && lines_skipped < lines_skippable) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end, false).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + if (IsClippedEx(line_rect, 0)) + break; + + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end, false).x); + RenderText(pos, line, line_end, false); + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end, false).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + text_size.y = (pos - text_pos).y; + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size, 0.0f); + ItemAdd(bb, 0); + } +} + void ImGui::TextUnformatted(const char* text, const char* text_end) { TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -264,6 +378,22 @@ void ImGui::Text(const char* fmt, ...) va_end(args); } +void ImGui::TextNoHashHide(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const char* text, *text_end; + ImFormatStringToTempBufferV(&text, &text_end, fmt, args); + TextExNoHashHide(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); + + va_end(args); +} + void ImGui::TextV(const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); @@ -3668,7 +3798,188 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** return line_count; } -static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +static const ImWchar* CalcWordWrapPositionW(const ImFont* font, float scale, const ImWchar* text, const ImWchar* text_end, float wrap_width) +{ + + // Simple word-wrapping for English, not full-featured. Please submit failing cases! + // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) + + // For references, possible wrap point marked with ^ + // "aaa bbb, ccc,ddd. eee fff. ggg!" + // ^ ^ ^ ^ ^__ ^ ^ + + // List of hardcoded separators: .,;!?'" + + // Skip extra blanks after a line returns (that includes not counting them in width computation) + // e.g. "Hello world" --> "Hello" "World" + + // Cut words that cannot possibly fit within one line. + // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" + + float line_width = 0.0f; + float word_width = 0.0f; + float blank_width = 0.0f; + wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters + + const ImWchar* word_end = text; + const ImWchar* prev_word_end = NULL; + bool inside_word = true; + + const ImWchar* s = text; + while (s < text_end) + { + ImWchar c = (unsigned int)*s++; + if (c == 0) + break; + + if (c < 32) + { + if (c == '\n') + { + line_width = word_width = blank_width = 0.0f; + inside_word = true; + continue; + } + if (c == '\r') + { + continue; + } + } + + const float char_width = font->GetCharAdvance(c) * scale; + if (ImCharIsBlankW(c)) + { + if (inside_word) + { + line_width += blank_width; + blank_width = 0.0f; + word_end = s - 1; + } + blank_width += char_width; + inside_word = false; + } + else + { + word_width += char_width; + if (inside_word) + { + word_end = s; + } + else + { + prev_word_end = word_end; + line_width += word_width + blank_width; + word_width = blank_width = 0.0f; + } + + // Allow wrapping after punctuation. + inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"'); + } + + // We ignore blank width at the end of the line (they can be skipped) + if (line_width + word_width > wrap_width) + { + // Words that cannot possibly fit within an entire line will be cut anywhere. + if (word_width < wrap_width) + s = prev_word_end ? prev_word_end : word_end; + break; + } + } + + return s; +} + + +static ImVec2 FindCharPosition(const ImWchar* text_begin, const ImWchar* char_position, const ImWchar* text_end, const float wrap_width) +{ + ImGuiContext& g = *GImGui; + ImFont* font = g.Font; + const float line_height = g.FontSize; + const float scale = line_height / font->FontSize; + + ImVec2 text_size = ImVec2(0, 0); + float line_width = 0.0f; + + const bool word_wrap_enabled = (wrap_width > 0.0f); + const ImWchar* word_wrap_eol = NULL; + + bool stop_on_new_line = false; + + const ImWchar* s = text_begin; + while (s < text_end) + { + if (word_wrap_enabled) + { + if (!word_wrap_eol) + { + word_wrap_eol = CalcWordWrapPositionW(font, scale, s, text_end, wrap_width - line_width); + if (word_wrap_eol == s) + { + word_wrap_eol++; + } + } + + if (s >= word_wrap_eol) + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + word_wrap_eol = NULL; + + + while (s < text_end) + { + unsigned int c = (unsigned int)(*s); + + if(ImCharIsBlankW(c)) { + s++; + } else if(c == '\n'){ + s++; + break; + } else { break; } + } + + if(s >= char_position) + { + stop_on_new_line = true; + } + + if (stop_on_new_line) + break; + continue; + } + } + + if(s == char_position) + { + break; + } + + unsigned int c = (unsigned int)(*s++); + + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (stop_on_new_line) + break; + continue; + } + if (c == '\r') + continue; + + const float char_width = font->GetCharAdvance((ImWchar)c) * scale; + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + return ImVec2(line_width, text_size.y + line_height); +} + +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset,const bool stop_on_new_line, const float word_wrap_width, const bool keep_trailing_blanks) { ImGuiContext& g = *ctx; ImFont* font = g.Font; @@ -3678,9 +3989,52 @@ static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begi ImVec2 text_size = ImVec2(0, 0); float line_width = 0.0f; + const float wrap_width = word_wrap_width; + const bool word_wrap_enabled = (wrap_width > 0.0f); + const ImWchar* word_wrap_eol = NULL; + const ImWchar* s = text_begin; while (s < text_end) { + if (word_wrap_enabled) + { + if (!word_wrap_eol) + { + word_wrap_eol = CalcWordWrapPositionW(font, scale, s, text_end, wrap_width - line_width); + if (word_wrap_eol == s) + { + word_wrap_eol++; + } + } + + if (s >= word_wrap_eol) + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + word_wrap_eol = NULL; + + + if(!keep_trailing_blanks) + { + while (s < text_end) + { + unsigned int c = (unsigned int)(*s); + if(ImCharIsBlankW(c)) { + s++; + } else if(c == '\n'){ + s++; + break; + } else { break; } + } + } + + if (stop_on_new_line) + break; + continue; + } + } + unsigned int c = (unsigned int)(*s++); if (c == '\n') { @@ -3726,7 +4080,7 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true, obj->WordWrapWidth); r->x0 = 0.0f; r->x1 = size.x; r->baseline_y_delta = size.y; @@ -4136,6 +4490,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size); + const bool has_word_wrap = is_multiline && (flags & ImGuiInputTextFlags_WordWrapping) != 0; + const float word_wrap_width = has_word_wrap ? frame_size.x - style.FramePadding.x * 2.0f : 0.0f; + ImGuiWindow* draw_window = window; ImVec2 inner_size = frame_size; ImGuiItemStatusFlags item_status_flags = 0; @@ -4155,13 +4512,22 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. - PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); + if(!style.DoFrameShadingForMultilineText) + { + PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); + } + PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); PopStyleVar(3); - PopStyleColor(); + + if(!style.DoFrameShadingForMultilineText) + { + PopStyleColor(); + } + if (!child_visible) { EndChild(); @@ -4259,6 +4625,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) } + if(state) + { + state->WordWrapWidth = word_wrap_width; + } + const bool is_osx = io.ConfigMacOSXBehaviors; if (g.ActiveId != id && init_make_active) { @@ -4822,7 +5193,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ g.WantTextInputNextFrame = 1; // Render frame - if (!is_multiline) + if (!is_multiline || (is_multiline && style.DoFrameShadingForMultilineText)) { RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); @@ -4860,63 +5231,35 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. const ImWchar* text_begin = state->TextW.Data; + const ImWchar* text_end = state->TextW.Data + ImStrlenW(state->TextW.Data); ImVec2 cursor_offset, select_start_offset; { - // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions. - const ImWchar* searches_input_ptr[2] = { NULL, NULL }; - int searches_result_line_no[2] = { -1000, -1000 }; - int searches_remaining = 0; - if (render_cursor) - { - searches_input_ptr[0] = text_begin + state->Stb.cursor; - searches_result_line_no[0] = -1; - searches_remaining++; + // Find input local positions of 'cursor' (slot 0) and 'select_start' (slot 1) positions. + if(render_cursor) { + const ImWchar* cursor_ptr = text_begin + state->Stb.cursor; + ImVec2 cursor_pos = FindCharPosition(text_begin, cursor_ptr, text_end, word_wrap_width); + cursor_offset.x = cursor_pos.x; + cursor_offset.y = cursor_pos.y; } if (render_selection) { - searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); - searches_result_line_no[1] = -1; - searches_remaining++; - } - - // Iterate all lines to find our line numbers - // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. - searches_remaining += is_multiline ? 1 : 0; - int line_count = 0; - //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++) // FIXME-OPT: Could use this when wchar_t are 16-bit - for (const ImWchar* s = text_begin; *s != 0; s++) - if (*s == '\n') - { - line_count++; - if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; } - } - line_count++; - if (searches_result_line_no[0] == -1) - searches_result_line_no[0] = line_count; - if (searches_result_line_no[1] == -1) - searches_result_line_no[1] = line_count; - - // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_no[0] * g.FontSize; - if (searches_result_line_no[1] >= 0) - { - select_start_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_no[1] * g.FontSize; + const ImWchar* selection_ptr = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + ImVec2 selection_pos = FindCharPosition(text_begin, selection_ptr, text_end, word_wrap_width); + select_start_offset.x = selection_pos.x; + select_start_offset.y = selection_pos.y; } // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) if (is_multiline) - text_size = ImVec2(inner_size.x, line_count * g.FontSize); + text_size = InputTextCalcTextSizeW(GImGui, text_begin, text_end, NULL, NULL, false, word_wrap_width); } // Scroll if (render_cursor && state->CursorFollow) { // Horizontal scroll in chunks of quarter width - if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) + if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll) && !has_word_wrap) { const float scroll_increment_x = inner_size.x * 0.25f; const float visible_width = inner_size.x - style.FramePadding.x; @@ -4962,7 +5305,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (rect_pos.y > clip_rect.w + g.FontSize) break; - if (rect_pos.y < clip_rect.y) + if (!has_word_wrap && rect_pos.y < clip_rect.y) { //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); // FIXME-OPT: Could use this when wchar_t are 16-bit //p = p ? p + 1 : text_selected_end; @@ -4972,7 +5315,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else { - ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true); + float selection_word_wrap = has_word_wrap ? (word_wrap_width - ImMax(0.0f, rect_pos.x - draw_pos.x)) : 0.0f; + ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true, selection_word_wrap, false); if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); rect.ClipWith(clip_rect); @@ -4988,7 +5332,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) { ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); - draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + draw_window->DrawList->AddTextNoHashHide(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, word_wrap_width, is_multiline ? NULL : &clip_rect); } // Draw blinking cursor @@ -5024,7 +5368,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) { ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); - draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + draw_window->DrawList->AddTextNoHashHide(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, word_wrap_width, is_multiline ? NULL : &clip_rect); } } @@ -6495,7 +6839,17 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. ImGuiID id = window->GetID(label); - ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 label_size; + + if(flags & ImGuiSelectableFlags_NoHashTextHide) + { + label_size = CalcTextSize(label, NULL, false); + } + else + { + label_size = CalcTextSize(label, NULL, true); + } + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); ImVec2 pos = window->DC.CursorPos; pos.y += window->DC.CurrLineTextBaseOffset; @@ -6612,7 +6966,14 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl else if (span_all_columns && g.CurrentTable) TablePopBackgroundChannel(); - RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + if(flags & ImGuiSelectableFlags_NoHashTextHide) + { + RenderTextClippedNoHashHide(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + } + else + { + RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + } // Automatically close popups if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) @@ -8023,7 +8384,7 @@ ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { if (tab->Window) - return tab->Window->Name; + return (tab->Window->DisplayedName ? tab->Window->DisplayedName : tab->Window->Name); if (tab->NameOffset == -1) return "N/A"; IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size); @@ -8702,7 +9063,7 @@ ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsave ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window) { - return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument)); + return TabItemCalcSize((window->DisplayedName == NULL ? window->Name : window->DisplayedName), window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument)); } void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) diff --git a/extern/imgui_patched/imstb_textedit.h b/extern/imgui_patched/imstb_textedit.h index 360395cee..d1d6b0d21 100644 --- a/extern/imgui_patched/imstb_textedit.h +++ b/extern/imgui_patched/imstb_textedit.h @@ -889,7 +889,7 @@ retry: // [DEAR IMGUI] // going down while being on the last line shouldn't bring us to that line end - if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) + if (!str->HasWordWrap() && STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) break; // now find character position down a row