Forked from ocornut/(Archived) imgui_memory_editor.h
Created
January 30, 2016 12:56
-
-
Save EEmmanuel7/0522eb014a240280f7be to your computer and use it in GitHub Desktop.
Mini memory editor for ImGui (to embed in your game/tools)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Mini memory editor for ImGui (to embed in your game/tools) | |
// v0.10 | |
// Animated gif: https://cloud.githubusercontent.com/assets/8225057/9028162/3047ef88-392c-11e5-8270-a54f8354b208.gif | |
// | |
// You can adjust the keyboard repeat delay/rate in ImGuiIO. | |
// The code assume a mono-space font for simplicity! If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before caling this. | |
// | |
// Usage: | |
// static MemoryEditor memory_editor; // save your state somewhere | |
// memory_editor.Draw("Memory Editor", mem_block, mem_block_size, (size_t)mem_block); // run | |
// | |
// TODO: better resizing policy (ImGui doesn't have flexible window resizing constraints yet) | |
struct MemoryEditor | |
{ | |
bool Open; | |
bool AllowEdits; | |
int Rows; | |
int DataEditingAddr; | |
bool DataEditingTakeFocus; | |
char DataInput[32]; | |
char AddrInput[32]; | |
MemoryEditor() | |
{ | |
Open = true; | |
Rows = 16; | |
DataEditingAddr = -1; | |
DataEditingTakeFocus = false; | |
strcpy(DataInput, ""); | |
strcpy(AddrInput, ""); | |
AllowEdits = true; | |
} | |
void Draw(const char* title, unsigned char* mem_data, int mem_size, size_t base_display_addr = 0) | |
{ | |
if (ImGui::Begin(title, &Open)) | |
{ | |
ImGui::BeginChild("##scrolling", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing())); | |
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); | |
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); | |
int addr_digits_count = 0; | |
for (int n = base_display_addr + mem_size - 1; n > 0; n >>= 4) | |
addr_digits_count++; | |
float glyph_width = ImGui::CalcTextSize("F").x; | |
float cell_width = glyph_width * 3; // "FF " we include trailing space in the width to easily catch clicks everywhere | |
float line_height = ImGui::GetTextLineHeight(); | |
int line_total_count = (int)((mem_size + Rows-1) / Rows); | |
ImGuiListClipper clipper(line_total_count, line_height); | |
int visible_start_addr = clipper.DisplayStart * Rows; | |
int visible_end_addr = clipper.DisplayEnd * Rows; | |
bool data_next = false; | |
if (!AllowEdits || DataEditingAddr >= mem_size) | |
DataEditingAddr = -1; | |
int data_editing_addr_backup = DataEditingAddr; | |
if (DataEditingAddr != -1) | |
{ | |
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataEditingAddr >= Rows) { DataEditingAddr -= Rows; DataEditingTakeFocus = true; } | |
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Rows) { DataEditingAddr += Rows; DataEditingTakeFocus = true; } | |
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { DataEditingAddr -= 1; DataEditingTakeFocus = true; } | |
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { DataEditingAddr += 1; DataEditingTakeFocus = true; } | |
} | |
if ((DataEditingAddr / Rows) != (data_editing_addr_backup / Rows)) | |
{ | |
// Track cursor movements | |
float scroll_offset = ((DataEditingAddr / Rows) - (data_editing_addr_backup / Rows)) * line_height; | |
bool scroll_desired = (scroll_offset < 0.0f && DataEditingAddr < visible_start_addr + Rows*2) || (scroll_offset > 0.0f && DataEditingAddr > visible_end_addr - Rows*2); | |
if (scroll_desired) | |
ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset); | |
} | |
bool draw_separator = true; | |
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible items | |
{ | |
int addr = line_i * Rows; | |
ImGui::Text("%0*X: ", addr_digits_count, base_display_addr+addr); | |
ImGui::SameLine(); | |
// Draw Hexadecimal | |
float line_start_x = ImGui::GetCursorPosX(); | |
for (int n = 0; n < Rows && addr < mem_size; n++, addr++) | |
{ | |
ImGui::SameLine(line_start_x + cell_width * n); | |
if (DataEditingAddr == addr) | |
{ | |
// Display text input on current byte | |
ImGui::PushID(addr); | |
struct FuncHolder | |
{ | |
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. | |
static int Callback(ImGuiTextEditCallbackData* data) | |
{ | |
int* p_cursor_pos = (int*)data->UserData; | |
if (!data->HasSelection()) | |
*p_cursor_pos = data->CursorPos; | |
return 0; | |
} | |
}; | |
int cursor_pos = -1; | |
bool data_write = false; | |
if (DataEditingTakeFocus) | |
{ | |
ImGui::SetKeyboardFocusHere(); | |
sprintf(AddrInput, "%0*X", addr_digits_count, base_display_addr+addr); | |
sprintf(DataInput, "%02X", mem_data[addr]); | |
} | |
ImGui::PushItemWidth(ImGui::CalcTextSize("FF").x); | |
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal|ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_AutoSelectAll|ImGuiInputTextFlags_NoHorizontalScroll|ImGuiInputTextFlags_AlwaysInsertMode|ImGuiInputTextFlags_CallbackAlways; | |
if (ImGui::InputText("##data", DataInput, 32, flags, FuncHolder::Callback, &cursor_pos)) | |
data_write = data_next = true; | |
else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) | |
DataEditingAddr = -1; | |
DataEditingTakeFocus = false; | |
ImGui::PopItemWidth(); | |
if (cursor_pos >= 2) | |
data_write = data_next = true; | |
if (data_write) | |
{ | |
int data; | |
if (sscanf(DataInput, "%X", &data) == 1) | |
mem_data[addr] = (unsigned char)data; | |
} | |
ImGui::PopID(); | |
} | |
else | |
{ | |
ImGui::Text("%02X ", mem_data[addr]); | |
if (AllowEdits && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)) | |
{ | |
DataEditingTakeFocus = true; | |
DataEditingAddr = addr; | |
} | |
} | |
} | |
ImGui::SameLine(line_start_x + cell_width * Rows + glyph_width * 2); | |
if (draw_separator) | |
{ | |
ImVec2 screen_pos = ImGui::GetCursorScreenPos(); | |
ImGui::GetWindowDrawList()->AddLine(ImVec2(screen_pos.x - glyph_width, screen_pos.y - 9999), ImVec2(screen_pos.x - glyph_width, screen_pos.y + 9999), ImColor(ImGui::GetStyle().Colors[ImGuiCol_Border])); | |
draw_separator = false; | |
} | |
// Draw ASCII values | |
addr = line_i * Rows; | |
for (int n = 0; n < Rows && addr < mem_size; n++, addr++) | |
{ | |
if (n > 0) ImGui::SameLine(); | |
int c = mem_data[addr]; | |
ImGui::Text("%c", (c >= 32 && c < 128) ? c : '.'); | |
} | |
} | |
clipper.End(); | |
ImGui::PopStyleVar(2); | |
ImGui::EndChild(); | |
if (data_next && DataEditingAddr < mem_size) | |
{ | |
DataEditingAddr = DataEditingAddr + 1; | |
DataEditingTakeFocus = true; | |
} | |
ImGui::Separator(); | |
ImGui::AlignFirstTextHeightToWidgets(); | |
ImGui::PushItemWidth(50); | |
ImGui::PushAllowKeyboardFocus(false); | |
int rows_backup = Rows; | |
if (ImGui::DragInt("##rows", &Rows, 0.2f, 4, 32, "%.0f rows")) | |
{ | |
ImVec2 new_window_size = ImGui::GetWindowSize(); | |
new_window_size.x += (Rows - rows_backup) * (cell_width + glyph_width); | |
ImGui::SetWindowSize(new_window_size); | |
} | |
ImGui::PopAllowKeyboardFocus(); | |
ImGui::PopItemWidth(); | |
ImGui::SameLine(); | |
ImGui::Text("Range %0*X..%0*X", addr_digits_count, (int)base_display_addr, addr_digits_count, (int)base_display_addr+mem_size-1); | |
ImGui::SameLine(); | |
ImGui::PushItemWidth(70); | |
if (ImGui::InputText("##addr", AddrInput, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) | |
{ | |
int goto_addr; | |
if (sscanf(AddrInput, "%X", &goto_addr) == 1) | |
{ | |
goto_addr -= base_display_addr; | |
if (goto_addr >= 0 && goto_addr < mem_size) | |
{ | |
ImGui::BeginChild("##scrolling"); | |
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (goto_addr / Rows) * ImGui::GetTextLineHeight()); | |
ImGui::EndChild(); | |
DataEditingAddr = goto_addr; | |
DataEditingTakeFocus = true; | |
} | |
} | |
} | |
ImGui::PopItemWidth(); | |
} | |
ImGui::End(); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment