From 8ae54084cf196c863d7af629512b0a254258020e Mon Sep 17 00:00:00 2001 From: Peter Kristensen Date: Sun, 27 Mar 2022 11:35:25 +0200 Subject: [PATCH] Update imgui_memory_editor to v0.50 --- imgui_memory_editor.h | 287 ++++++++++++++++++++---------------------- 1 file changed, 139 insertions(+), 148 deletions(-) diff --git a/imgui_memory_editor.h b/imgui_memory_editor.h index 46c46fb5..ec2beef7 100644 --- a/imgui_memory_editor.h +++ b/imgui_memory_editor.h @@ -38,9 +38,11 @@ // - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out. // - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel] // - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width. +// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size. // // Todo/Bugs: -// - This is generally old code, it should work but please don't use this as reference! +// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day. +// - PageUp/PageDown are supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own... // - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame. // - Using InputText() is awkward and maybe overkill here, consider implementing something custom. @@ -186,6 +188,7 @@ struct MemoryEditor { Sizes s; CalcSizes(s, mem_size, base_display_addr); + ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); Open = true; @@ -232,9 +235,6 @@ struct MemoryEditor const int line_total_count = (int)((mem_size + Cols - 1) / Cols); ImGuiListClipper clipper; clipper.Begin(line_total_count, s.LineHeight); - clipper.Step(); - const size_t visible_start_addr = clipper.DisplayStart * Cols; - const size_t visible_end_addr = clipper.DisplayEnd * Cols; bool data_next = false; @@ -245,23 +245,14 @@ struct MemoryEditor size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0; - size_t data_editing_addr_backup = DataEditingAddr; size_t data_editing_addr_next = (size_t)-1; if (DataEditingAddr != (size_t)-1) { // Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered) - if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataEditingAddr >= (size_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { data_editing_addr_next = DataEditingAddr - 1; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; DataEditingTakeFocus = true; } - } - if (data_editing_addr_next != (size_t)-1 && (data_editing_addr_next / Cols) != (data_editing_addr_backup / Cols)) - { - // Track cursor movements - const int scroll_offset = ((int)(data_editing_addr_next / Cols) - (int)(data_editing_addr_backup / Cols)); - const bool scroll_desired = (scroll_offset < 0 && data_editing_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_editing_addr_next > visible_end_addr - Cols * 2); - if (scroll_desired) - ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight); + if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; } + else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; } + else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; } + else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; } } // Draw vertical separator @@ -277,167 +268,166 @@ struct MemoryEditor const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x"; const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x "; - for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines - { - size_t addr = (size_t)(line_i * Cols); - ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr); - - // Draw Hexadecimal - for (int n = 0; n < Cols && addr < mem_size; n++, addr++) + while (clipper.Step()) + for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines { - float byte_pos_x = s.PosHexStart + s.HexCellWidth * n; - if (OptMidColsCount > 0) - byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols; - ImGui::SameLine(byte_pos_x); - - // Draw highlight - bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); - bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr)); - bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size); - if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) - { - ImVec2 pos = ImGui::GetCursorScreenPos(); - float highlight_width = s.GlyphWidth * 2; - bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1))); - if (is_next_byte_highlighted || (n + 1 == Cols)) - { - highlight_width = s.HexCellWidth; - if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) - highlight_width += s.SpacingBetweenMidCols; - } - draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor); - } + size_t addr = (size_t)(line_i * Cols); + ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr); - if (DataEditingAddr == addr) + // Draw Hexadecimal + for (int n = 0; n < Cols && addr < mem_size; n++, addr++) { - // Display text input on current byte - bool data_write = false; - ImGui::PushID((void*)addr); - if (DataEditingTakeFocus) + float byte_pos_x = s.PosHexStart + s.HexCellWidth * n; + if (OptMidColsCount > 0) + byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols; + ImGui::SameLine(byte_pos_x); + + // Draw highlight + bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); + bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr)); + bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size); + if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) { - ImGui::SetKeyboardFocusHere(); - ImGui::CaptureKeyboardFromApp(true); - sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr); - sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]); + ImVec2 pos = ImGui::GetCursorScreenPos(); + float highlight_width = s.GlyphWidth * 2; + bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1))); + if (is_next_byte_highlighted || (n + 1 == Cols)) + { + highlight_width = s.HexCellWidth; + if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) + highlight_width += s.SpacingBetweenMidCols; + } + draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor); } - struct UserData + + if (DataEditingAddr == addr) { - // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here. - static int Callback(ImGuiInputTextCallbackData* data) + // Display text input on current byte + bool data_write = false; + ImGui::PushID((void*)addr); + if (DataEditingTakeFocus) { - UserData* user_data = (UserData*)data->UserData; - if (!data->HasSelection()) - user_data->CursorPos = data->CursorPos; - if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen) + ImGui::SetKeyboardFocusHere(0); + sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr); + sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]); + } + struct UserData + { + // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here. + static int Callback(ImGuiInputTextCallbackData* data) { - // When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there) - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, user_data->CurrentBufOverwrite); - data->SelectionStart = 0; - data->SelectionEnd = 2; - data->CursorPos = 0; + UserData* user_data = (UserData*)data->UserData; + if (!data->HasSelection()) + user_data->CursorPos = data->CursorPos; + if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen) + { + // When not editing a byte, always refresh its InputText content pulled from underlying memory data + // (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there) + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, user_data->CurrentBufOverwrite); + data->SelectionStart = 0; + data->SelectionEnd = 2; + data->CursorPos = 0; + } + return 0; } - return 0; - } - char CurrentBufOverwrite[3]; // Input - int CursorPos; // Output - }; - UserData user_data; - user_data.CursorPos = -1; - sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways; + char CurrentBufOverwrite[3]; // Input + int CursorPos; // Output + }; + UserData user_data; + user_data.CursorPos = -1; + sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways; #if IMGUI_VERSION_NUM >= 18104 - flags |= ImGuiInputTextFlags_AlwaysOverwrite; + flags |= ImGuiInputTextFlags_AlwaysOverwrite; #else - flags |= ImGuiInputTextFlags_AlwaysInsertMode; + flags |= ImGuiInputTextFlags_AlwaysInsertMode; #endif - ImGui::SetNextItemWidth(s.GlyphWidth * 2); - if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, UserData::Callback, &user_data)) - data_write = data_next = true; - else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) - DataEditingAddr = data_editing_addr_next = (size_t)-1; - DataEditingTakeFocus = false; - if (user_data.CursorPos >= 2) - data_write = data_next = true; - if (data_editing_addr_next != (size_t)-1) - data_write = data_next = false; - unsigned int data_input_value = 0; - if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) - { - if (WriteFn) - WriteFn(mem_data, addr, (ImU8)data_input_value); - else - mem_data[addr] = (ImU8)data_input_value; - } - ImGui::PopID(); - } - else - { - // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on. - ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; - - if (OptShowHexII) - { - if ((b >= 32 && b < 128)) - ImGui::Text(".%c ", b); - else if (b == 0xFF && OptGreyOutZeroes) - ImGui::TextDisabled("## "); - else if (b == 0x00) - ImGui::Text(" "); - else - ImGui::Text(format_byte_space, b); + ImGui::SetNextItemWidth(s.GlyphWidth * 2); + if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, UserData::Callback, &user_data)) + data_write = data_next = true; + else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) + DataEditingAddr = data_editing_addr_next = (size_t)-1; + DataEditingTakeFocus = false; + if (user_data.CursorPos >= 2) + data_write = data_next = true; + if (data_editing_addr_next != (size_t)-1) + data_write = data_next = false; + unsigned int data_input_value = 0; + if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) + { + if (WriteFn) + WriteFn(mem_data, addr, (ImU8)data_input_value); + else + mem_data[addr] = (ImU8)data_input_value; + } + ImGui::PopID(); } else { - if (b == 0 && OptGreyOutZeroes) - ImGui::TextDisabled("00 "); + // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on. + ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; + + if (OptShowHexII) + { + if ((b >= 32 && b < 128)) + ImGui::Text(".%c ", b); + else if (b == 0xFF && OptGreyOutZeroes) + ImGui::TextDisabled("## "); + else if (b == 0x00) + ImGui::Text(" "); + else + ImGui::Text(format_byte_space, b); + } else - ImGui::Text(format_byte_space, b); - } - if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)) - { - DataEditingTakeFocus = true; - data_editing_addr_next = addr; + { + if (b == 0 && OptGreyOutZeroes) + ImGui::TextDisabled("00 "); + else + ImGui::Text(format_byte_space, b); + } + if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)) + { + DataEditingTakeFocus = true; + data_editing_addr_next = addr; + } } } - } - if (OptShowAscii) - { - // Draw ASCII values - ImGui::SameLine(s.PosAsciiStart); - ImVec2 pos = ImGui::GetCursorScreenPos(); - addr = line_i * Cols; - ImGui::PushID(line_i); - if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight))) + if (OptShowAscii) { - DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth); - DataEditingTakeFocus = true; - } - ImGui::PopID(); - for (int n = 0; n < Cols && addr < mem_size; n++, addr++) - { - if (addr == DataEditingAddr) + // Draw ASCII values + ImGui::SameLine(s.PosAsciiStart); + ImVec2 pos = ImGui::GetCursorScreenPos(); + addr = line_i * Cols; + ImGui::PushID(line_i); + if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight))) + { + DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth); + DataEditingTakeFocus = true; + } + ImGui::PopID(); + for (int n = 0; n < Cols && addr < mem_size; n++, addr++) { - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); + if (addr == DataEditingAddr) + { + draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); + draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); + } + unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; + char display_c = (c < 32 || c >= 128) ? '.' : c; + draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1); + pos.x += s.GlyphWidth; } - unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; - char display_c = (c < 32 || c >= 128) ? '.' : c; - draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1); - pos.x += s.GlyphWidth; } } - } - IM_ASSERT(clipper.Step() == false); - clipper.End(); ImGui::PopStyleVar(2); ImGui::EndChild(); // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child) ImGui::SetCursorPosX(s.WindowWidth); - if (data_next && DataEditingAddr < mem_size) + if (data_next && DataEditingAddr + 1 < mem_size) { DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1; DataEditingTakeFocus = true; @@ -445,6 +435,7 @@ struct MemoryEditor else if (data_editing_addr_next != (size_t)-1) { DataEditingAddr = DataPreviewAddr = data_editing_addr_next; + DataEditingTakeFocus = true; } const bool lock_show_data_preview = OptShowDataPreview;