Last active
August 25, 2021 01:51
-
-
Save Jjagg/31de963c579a2118a3b7c0764d8abe45 to your computer and use it in GitHub Desktop.
MonoGame backend sample for ImGui.NET (https://github.com/mellinoe/ImGui.NET)
This file contains 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
using System; | |
using System.Runtime.InteropServices; | |
using ImGuiNET; | |
using Microsoft.Xna.Framework; | |
using Microsoft.Xna.Framework.Input; | |
using Vector2 = ImGuiNET.Vector2; | |
using Vector3 = ImGuiNET.Vector3; | |
using Vector4 = ImGuiNET.Vector4; | |
namespace ImGuiMG | |
{ | |
public class Game1 : Game | |
{ | |
private readonly GraphicsDeviceManager _graphics; | |
private ImGuiMg _imgui; | |
private int _pressCount; | |
private Vector4 _buttonColor = new Vector4(55f / 255f, 155f / 255f, 1f, 1f); | |
private bool _mainWindowOpened; | |
private float _sliderVal; | |
private Vector3 _positionValue = new Vector3(500); | |
public readonly IntPtr TextInputBuffer; | |
public readonly int TextInputBufferLength; | |
private readonly MemoryEditor _memoryEditor = new MemoryEditor(); | |
private readonly byte[] _memoryEditorData; | |
public unsafe Game1() | |
{ | |
_graphics = new GraphicsDeviceManager(this); | |
Content.RootDirectory = "Content"; | |
IsMouseVisible = true; | |
Window.AllowUserResizing = true; | |
IsFixedTimeStep = false; | |
_graphics.SynchronizeWithVerticalRetrace = false; | |
_graphics.PreferredBackBufferWidth = 960; | |
_graphics.PreferredBackBufferHeight = 540; | |
TextInputBufferLength = 1024; | |
TextInputBuffer = Marshal.AllocHGlobal(TextInputBufferLength); | |
var ptr = (long*)TextInputBuffer.ToPointer(); | |
for (var i = 0; i < 1024 / sizeof(long); i++) | |
ptr[i] = 0; | |
_memoryEditorData = new byte[1024]; | |
var rnd = new Random(); | |
for (var i = 0; i < _memoryEditorData.Length; i++) | |
_memoryEditorData[i] = (byte) rnd.Next(255); | |
} | |
protected override void LoadContent() | |
{ | |
_imgui = new ImGuiMg(Window, GraphicsDevice); | |
/*var frc = new FrameRateCounter(this); | |
frc.Position = new Microsoft.Xna.Framework.Vector2(10f, 500f); | |
frc.Font = Content.Load<SpriteFont>("font"); | |
Components.Add(frc);*/ | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
_imgui.Dispose(); | |
base.Dispose(disposing); | |
} | |
protected override void Update(GameTime gameTime) | |
{ | |
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) | |
Exit(); | |
_imgui.Update(gameTime); | |
SubmitImGuiStuff(); | |
base.Update(gameTime); | |
} | |
protected override void Draw(GameTime gameTime) | |
{ | |
GraphicsDevice.Clear(Color.CornflowerBlue); | |
_imgui.Draw(); | |
base.Draw(gameTime); | |
} | |
private unsafe void SubmitImGuiStuff() | |
{ | |
ImGui.GetStyle().WindowRounding = 0; | |
var pp = GraphicsDevice.PresentationParameters; | |
ImGui.SetNextWindowSize(new Vector2(pp.BackBufferWidth - 20, pp.BackBufferHeight - 50), SetCondition.Once); | |
ImGui.SetNextWindowPosCenter(SetCondition.Always); | |
ImGui.BeginWindow("Hello from ImGui!", ref _mainWindowOpened, WindowFlags.Default); | |
ImGui.BeginMainMenuBar(); | |
if (ImGui.BeginMenu("Help")) | |
{ | |
if (ImGui.MenuItem("About", "Ctrl-Alt-A", false, true)) | |
{ | |
} | |
ImGui.EndMenu(); | |
} | |
ImGui.EndMainMenuBar(); | |
var pos = ImGui.GetIO().MousePosition; | |
var leftPressed = ImGui.GetIO().MouseDown[0]; | |
ImGui.Text("Current mouse position: " + pos + ". Pressed=" + leftPressed); | |
if (ImGui.Button("Increment the counter.")) | |
_pressCount += 1; | |
ImGui.Text($"Button pressed {_pressCount} times.", new Vector4(0, 1, 1, 1)); | |
ImGui.Text("Input some text:"); | |
ImGui.InputTextMultiline(string.Empty, | |
TextInputBuffer, (uint) TextInputBufferLength, | |
new Vector2(360, 240), | |
InputTextFlags.Default, | |
OnTextEdited); | |
ImGui.SliderFloat("SlidableValue", ref _sliderVal, -50f, 100f, $"{_sliderVal:##0.00}", 1.0f); | |
ImGui.DragVector3("Vector3", ref _positionValue, -100, 100); | |
if (ImGui.TreeNode("First Item")) | |
{ | |
ImGui.Text("Word!"); | |
ImGui.TreePop(); | |
} | |
if (ImGui.TreeNode("Second Item")) | |
{ | |
ImGui.ColorButton(_buttonColor, false, true); | |
if (ImGui.Button("Push me to change color", new Vector2(120, 30))) | |
{ | |
_buttonColor = new Vector4(_buttonColor.Y + .25f, _buttonColor.Z, _buttonColor.X, _buttonColor.W); | |
if (_buttonColor.X > 1.0f) | |
{ | |
_buttonColor.X -= 1.0f; | |
} | |
} | |
ImGui.TreePop(); | |
} | |
if (ImGui.Button("Press me!", new Vector2(100, 30))) | |
ImGuiNative.igOpenPopup("SmallButtonPopup"); | |
if (ImGui.BeginPopup("SmallButtonPopup")) | |
{ | |
ImGui.Text("Here's a popup menu."); | |
ImGui.Text("With two lines."); | |
ImGui.EndPopup(); | |
} | |
if (ImGui.Button("Open Modal window")) | |
ImGui.OpenPopup("ModalPopup"); | |
if (ImGui.BeginPopupModal("ModalPopup")) | |
{ | |
ImGui.Text("You can't press on anything else right now."); | |
ImGui.Text("You are stuck here."); | |
if (ImGui.Button("OK", new Vector2(0, 0))) { } | |
ImGui.SameLine(); | |
ImGui.Dummy(100f, 0f); | |
ImGui.SameLine(); | |
if (ImGui.Button("Please go away", new Vector2(0, 0))) { ImGui.CloseCurrentPopup(); } | |
ImGui.EndPopup(); | |
} | |
ImGui.Text("I have a context menu."); | |
if (ImGui.BeginPopupContextItem("ItemContextMenu")) | |
{ | |
if (ImGui.Selectable("How's this for a great menu?")) { } | |
ImGui.Selectable("Just click somewhere to get rid of me."); | |
ImGui.EndPopup(); | |
} | |
ImGui.EndWindow(); | |
_memoryEditor.Draw("Memory editor", _memoryEditorData, _memoryEditorData.Length); | |
} | |
public unsafe int OnTextEdited(TextEditCallbackData* data) | |
{ | |
var currentEventChar = (char)data->EventChar; | |
return 0; | |
} | |
} | |
} |
This file contains 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
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Microsoft.Xna.Framework; | |
using Microsoft.Xna.Framework.Graphics; | |
using Microsoft.Xna.Framework.Input; | |
namespace ImGuiNET | |
{ | |
public sealed class ImGuiMg : IDisposable | |
{ | |
private readonly GraphicsDevice _graphicsDevice; | |
private readonly List<Texture2D> _textures; | |
private readonly RasterizerState _rasterizerState; | |
private readonly SpriteEffect _spriteEffect; | |
private KeyboardState _prevKeyState; | |
private int _sFontTexture; | |
private float _wheelPosition; | |
private readonly float _scaleFactor; | |
public ImGuiMg(GameWindow gw, GraphicsDevice gd) | |
{ | |
_graphicsDevice = gd; | |
_textures = new List<Texture2D>(); | |
_rasterizerState = new RasterizerState | |
{ | |
CullMode = CullMode.None, | |
ScissorTestEnable = true | |
}; | |
_spriteEffect = new SpriteEffect(gd); | |
int desiredWidth = 960, desiredHeight = 540; | |
_scaleFactor = (float) gd.PresentationParameters.BackBufferWidth / desiredWidth; | |
ImGui.GetIO().FontAtlas.AddDefaultFont(); | |
SetKeyMappings(); | |
gw.TextInput += OnKeyPress; | |
CreateDeviceObjects(); | |
} | |
#region Keys | |
private void OnKeyPress(object sender, TextInputEventArgs e) | |
{ | |
Console.Write("Char typed: " + e.Character); | |
ImGui.AddInputCharacter(e.Character); | |
} | |
private static unsafe void SetKeyMappings() | |
{ | |
var io = ImGui.GetIO(); | |
io.KeyMap[GuiKey.Tab] = (int)Keys.Tab; | |
io.KeyMap[GuiKey.LeftArrow] = (int)Keys.Left; | |
io.KeyMap[GuiKey.RightArrow] = (int)Keys.Right; | |
io.KeyMap[GuiKey.UpArrow] = (int)Keys.Up; | |
io.KeyMap[GuiKey.DownArrow] = (int)Keys.Down; | |
io.KeyMap[GuiKey.PageUp] = (int)Keys.PageUp; | |
io.KeyMap[GuiKey.PageDown] = (int)Keys.PageDown; | |
io.KeyMap[GuiKey.Home] = (int)Keys.Home; | |
io.KeyMap[GuiKey.End] = (int)Keys.End; | |
io.KeyMap[GuiKey.Delete] = (int)Keys.Delete; | |
io.KeyMap[GuiKey.Backspace] = (int)Keys.Back; | |
io.KeyMap[GuiKey.Enter] = (int)Keys.Enter; | |
io.KeyMap[GuiKey.Escape] = (int)Keys.Escape; | |
io.KeyMap[GuiKey.A] = (int)Keys.A; | |
io.KeyMap[GuiKey.C] = (int)Keys.C; | |
io.KeyMap[GuiKey.V] = (int)Keys.V; | |
io.KeyMap[GuiKey.X] = (int)Keys.X; | |
io.KeyMap[GuiKey.Y] = (int)Keys.Y; | |
io.KeyMap[GuiKey.Z] = (int)Keys.Z; | |
} | |
private static void UpdateKeys(KeyboardState prev, KeyboardState current) | |
{ | |
// Keys down | |
foreach (var k in current.GetPressedKeys()) | |
ImGui.GetIO().KeysDown[(int) k] = true; | |
// Keys up | |
foreach (var k in prev.GetPressedKeys()) | |
{ | |
if (!current.GetPressedKeys().Contains(k)) | |
ImGui.GetIO().KeysDown[(int) k] = false; | |
} | |
UpdateModifiers(current); | |
} | |
private static void UpdateModifiers(KeyboardState state) | |
{ | |
var io = ImGui.GetIO(); | |
io.AltPressed = state.IsKeyDown(Keys.LeftAlt) || state.IsKeyDown(Keys.RightAlt); | |
io.CtrlPressed = state.IsKeyDown(Keys.LeftControl) || state.IsKeyDown(Keys.RightControl); | |
io.ShiftPressed = state.IsKeyDown(Keys.LeftShift) || state.IsKeyDown(Keys.RightShift); | |
} | |
#endregion | |
private unsafe void CreateDeviceObjects() | |
{ | |
var io = ImGui.GetIO(); | |
// Build texture atlas | |
var texData = io.FontAtlas.GetTexDataAsRGBA32(); | |
var tex = new Texture2D(_graphicsDevice, texData.Width, texData.Height); | |
if (texData.BytesPerPixel != 4) | |
throw new Exception(); | |
var pixelCount = texData.Width * texData.Height; | |
var pixelData = new Color[pixelCount]; | |
for (var i = 0; i < pixelCount; i++) | |
pixelData[i] = new Color((int) texData.Pixels[4 * i], (int) texData.Pixels[4 * i + 1], | |
(int) texData.Pixels[4 * i + 2], (int) texData.Pixels[4 * i + 3]); | |
tex.SetData(pixelData); | |
_sFontTexture = _textures.Count; | |
_textures.Add(tex); | |
io.FontAtlas.SetTexID(_sFontTexture); | |
// Store the texture identifier in the ImFontAtlas substructure. | |
io.FontAtlas.SetTexID(_sFontTexture); | |
// Cleanup (don't clear the input data if you want to append new fonts later) | |
//io.Fonts->ClearInputData(); | |
io.FontAtlas.ClearTexData(); | |
} | |
public void Update(GameTime gameTime) | |
{ | |
var io = ImGui.GetIO(); | |
io.DeltaTime = (float) gameTime.ElapsedGameTime.TotalSeconds; | |
UpdateImGuiInput(io); | |
var pp = _graphicsDevice.PresentationParameters; | |
io.DisplaySize = new Vector2(pp.BackBufferWidth, pp.BackBufferHeight); | |
io.DisplayFramebufferScale = new Vector2(_scaleFactor); | |
ImGui.NewFrame(); | |
} | |
public unsafe void Draw() | |
{ | |
ImGui.Render(); | |
var data = ImGui.GetDrawData(); | |
RenderImDrawData(data); | |
} | |
private void UpdateImGuiInput(IO io) | |
{ | |
var kbState = Keyboard.GetState(); | |
UpdateKeys(_prevKeyState, kbState); | |
var mouseState = Mouse.GetState(); | |
var windowPoint = mouseState.Position; | |
io.MousePosition = new Vector2(windowPoint.X / io.DisplayFramebufferScale.X, windowPoint.Y / io.DisplayFramebufferScale.Y); | |
io.MouseDown[0] = mouseState.LeftButton == ButtonState.Pressed; | |
io.MouseDown[1] = mouseState.RightButton == ButtonState.Pressed; | |
io.MouseDown[2] = mouseState.MiddleButton == ButtonState.Pressed; | |
float newWheelPos = mouseState.ScrollWheelValue; | |
var delta = newWheelPos - _wheelPosition; | |
_wheelPosition = newWheelPos; | |
io.MouseWheel = delta; | |
_prevKeyState = kbState; | |
} | |
private unsafe void RenderImDrawData(DrawData* drawData) | |
{ | |
if (drawData == null) | |
{ | |
Console.WriteLine("No frame rendered for ImGUI.NET, but draw was called."); | |
return; | |
} | |
var pp = _graphicsDevice.PresentationParameters; | |
// Rendering | |
var displayW = pp.BackBufferWidth; | |
var displayH = pp.BackBufferHeight; | |
var clearColor = new Vector4(114f / 255f, 144f / 255f, 154f / 255f, 1.0f); | |
_graphicsDevice.Viewport = new Viewport(0, 0, displayW, displayH); | |
_graphicsDevice.Clear(new Color(clearColor.X, clearColor.Y, clearColor.Z, clearColor.W)); | |
// We are using the OpenGL fixed pipeline to make the example code simpler to read! | |
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers. | |
_graphicsDevice.BlendState = BlendState.NonPremultiplied; | |
_graphicsDevice.RasterizerState = _rasterizerState; | |
_graphicsDevice.DepthStencilState = DepthStencilState.None; | |
// Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays) | |
var io = ImGui.GetIO(); | |
ImGui.ScaleClipRects(drawData, io.DisplayFramebufferScale); | |
// Render command lists | |
for (var n = 0; n < drawData->CmdListsCount; n++) | |
{ | |
var cmdList = drawData->CmdLists[n]; | |
var vtxBuffer = (DrawVert*)cmdList->VtxBuffer.Data; | |
var idxBuffer = (ushort*)cmdList->IdxBuffer.Data; | |
var vertices = new VertexPositionColorTexture[cmdList->VtxBuffer.Size]; | |
for (var i = 0; i < vertices.Length; i++) | |
{ | |
vertices[i] = new VertexPositionColorTexture( | |
new Microsoft.Xna.Framework.Vector3(vtxBuffer[i].pos.X, vtxBuffer[i].pos.Y, 0), | |
new Color(vtxBuffer[i].col), | |
new Microsoft.Xna.Framework.Vector2(vtxBuffer[i].uv.X, vtxBuffer[i].uv.Y)); | |
} | |
_spriteEffect.CurrentTechnique.Passes[0].Apply(); | |
for (var cmdI = 0; cmdI < cmdList->CmdBuffer.Size; cmdI++) | |
{ | |
var pcmd = &(((DrawCmd*)cmdList->CmdBuffer.Data)[cmdI]); | |
if (pcmd->UserCallback != IntPtr.Zero) | |
{ | |
throw new NotImplementedException(); | |
} | |
else | |
{ | |
_graphicsDevice.Textures[0] = _textures[pcmd->TextureId.ToInt32()]; | |
_graphicsDevice.ScissorRectangle = new Rectangle( | |
(int)pcmd->ClipRect.X, | |
(int)pcmd->ClipRect.Y, | |
(int)(pcmd->ClipRect.Z - pcmd->ClipRect.X), | |
(int)(pcmd->ClipRect.W - pcmd->ClipRect.Y)); | |
var indices = new short[pcmd->ElemCount]; | |
for (var i = 0; i < indices.Length; i++) { indices[i] = (short) idxBuffer[i]; } | |
_graphicsDevice.DrawUserIndexedPrimitives( | |
PrimitiveType.TriangleList, vertices, 0, | |
vertices.Length, indices, 0, (int) pcmd->ElemCount / 3); | |
} | |
idxBuffer += pcmd->ElemCount; | |
} | |
} | |
} | |
public void Dispose() | |
{ | |
_rasterizerState.Dispose(); | |
_spriteEffect.Dispose(); | |
} | |
} | |
} |
This file contains 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
using System; | |
using System.Globalization; | |
namespace ImGuiNET | |
{ | |
// C# port of ocornut's imgui_memory_editor.h - https://gist.github.com/ocornut/0673e37e54aff644298b | |
// 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 | |
public class MemoryEditor | |
{ | |
readonly bool _allowEdits; | |
int _rows; | |
int _dataEditingAddr; | |
bool _dataEditingTakeFocus; | |
readonly byte[] _dataInput = new byte[32]; | |
readonly byte[] _addrInput = new byte[32]; | |
public MemoryEditor() | |
{ | |
_rows = 16; | |
_dataEditingAddr = -1; | |
_dataEditingTakeFocus = false; | |
_allowEdits = true; | |
} | |
private static string FixedHex(int v, int count) | |
{ | |
return v.ToString("X").PadLeft(count, '0'); | |
} | |
private static bool TryHexParse(byte[] bytes, out int result) | |
{ | |
string input = System.Text.Encoding.UTF8.GetString(bytes).ToString(); | |
return int.TryParse(input, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out result); | |
} | |
private static void ReplaceChars(byte[] bytes, string input) | |
{ | |
var address = System.Text.Encoding.ASCII.GetBytes(input); | |
for (int i = 0; i < bytes.Length; i++) | |
{ | |
bytes[i] = (i < address.Length) ? address[i] : (byte)0; | |
} | |
} | |
public unsafe void Draw(string title, byte[] memData, int memSize, int baseDisplayAddr = 0) | |
{ | |
if (!ImGui.BeginWindow(title)) | |
{ | |
ImGui.EndWindow(); | |
return; | |
} | |
float lineHeight = ImGuiNative.igGetTextLineHeight(); | |
int lineTotalCount = (memSize + _rows - 1) / _rows; | |
ImGuiNative.igSetNextWindowContentSize(new Vector2(0.0f, lineTotalCount * lineHeight)); | |
ImGui.BeginChild("##scrolling", new Vector2(0, -ImGuiNative.igGetItemsLineHeightWithSpacing()), false, 0); | |
ImGui.PushStyleVar(StyleVar.FramePadding, new Vector2(0, 0)); | |
ImGui.PushStyleVar(StyleVar.ItemSpacing, new Vector2(0, 0)); | |
int addrDigitsCount = 0; | |
for (int n = baseDisplayAddr + memSize - 1; n > 0; n >>= 4) | |
addrDigitsCount++; | |
float glyphWidth = ImGui.GetTextSize("F").X; | |
float cellWidth = glyphWidth * 3; // "FF " we include trailing space in the width to easily catch clicks everywhere | |
var clipper = new ImGuiListClipper(lineTotalCount, lineHeight); | |
int visibleStartAddr = clipper.DisplayStart * _rows; | |
int visibleEndAddr = clipper.DisplayEnd * _rows; | |
bool dataNext = false; | |
if (!_allowEdits || _dataEditingAddr >= memSize) | |
_dataEditingAddr = -1; | |
int dataEditingAddrBackup = _dataEditingAddr; | |
if (_dataEditingAddr != -1) | |
{ | |
if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(GuiKey.UpArrow)) && _dataEditingAddr >= _rows) { _dataEditingAddr -= _rows; _dataEditingTakeFocus = true; } | |
else if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(GuiKey.DownArrow)) && _dataEditingAddr < memSize - _rows) { _dataEditingAddr += _rows; _dataEditingTakeFocus = true; } | |
else if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(GuiKey.LeftArrow)) && _dataEditingAddr > 0) { _dataEditingAddr -= 1; _dataEditingTakeFocus = true; } | |
else if (ImGui.IsKeyPressed(ImGui.GetKeyIndex(GuiKey.RightArrow)) && _dataEditingAddr < memSize - 1) { _dataEditingAddr += 1; _dataEditingTakeFocus = true; } | |
} | |
if ((_dataEditingAddr / _rows) != (dataEditingAddrBackup / _rows)) | |
{ | |
// Track cursor movements | |
float scrollOffset = ((_dataEditingAddr / _rows) - (dataEditingAddrBackup / _rows)) * lineHeight; | |
bool scrollDesired = (scrollOffset < 0.0f && _dataEditingAddr < visibleStartAddr + _rows * 2) || (scrollOffset > 0.0f && _dataEditingAddr > visibleEndAddr - _rows * 2); | |
if (scrollDesired) | |
ImGuiNative.igSetScrollY(ImGuiNative.igGetScrollY() + scrollOffset); | |
} | |
for (int lineI = clipper.DisplayStart; lineI < clipper.DisplayEnd; lineI++) // display only visible items | |
{ | |
int addr = lineI * _rows; | |
ImGui.Text(FixedHex(baseDisplayAddr + addr, addrDigitsCount) + ": "); | |
ImGui.SameLine(); | |
// Draw Hexadecimal | |
float lineStartX = ImGuiNative.igGetCursorPosX(); | |
for (int n = 0; n < _rows && addr < memSize; n++, addr++) | |
{ | |
ImGui.SameLine(lineStartX + cellWidth * n); | |
if (_dataEditingAddr == addr) | |
{ | |
// Display text input on current byte | |
ImGui.PushID(addr); | |
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. | |
TextEditCallback callback = (data) => | |
{ | |
int* pCursorPos = (int*)data->UserData; | |
if (!data->HasSelection()) | |
*pCursorPos = data->CursorPos; | |
return 0; | |
}; | |
int cursorPos = -1; | |
bool dataWrite = false; | |
if (_dataEditingTakeFocus) | |
{ | |
ImGui.SetKeyboardFocusHere(); | |
ReplaceChars(_dataInput, FixedHex(memData[addr], 2)); | |
ReplaceChars(_addrInput, FixedHex(baseDisplayAddr + addr, addrDigitsCount)); | |
} | |
ImGui.PushItemWidth(ImGui.GetTextSize("FF").X); | |
var flags = InputTextFlags.CharsHexadecimal | InputTextFlags.EnterReturnsTrue | InputTextFlags.AutoSelectAll | InputTextFlags.NoHorizontalScroll | InputTextFlags.AlwaysInsertMode | InputTextFlags.CallbackAlways; | |
if (ImGui.InputText("##data", _dataInput, 32, flags, callback, new IntPtr(&cursorPos))) | |
dataWrite = dataNext = true; | |
else if (!_dataEditingTakeFocus && !ImGui.IsLastItemActive()) | |
_dataEditingAddr = -1; | |
_dataEditingTakeFocus = false; | |
ImGui.PopItemWidth(); | |
if (cursorPos >= 2) | |
dataWrite = dataNext = true; | |
if (dataWrite) | |
{ | |
int data; | |
if (TryHexParse(_dataInput, out data)) | |
memData[addr] = (byte)data; | |
} | |
ImGui.PopID(); | |
} | |
else | |
{ | |
ImGui.Text(FixedHex(memData[addr], 2)); | |
if (_allowEdits && ImGui.IsLastItemHovered() && ImGui.IsMouseClicked(0)) | |
{ | |
_dataEditingTakeFocus = true; | |
_dataEditingAddr = addr; | |
} | |
} | |
} | |
ImGui.SameLine(lineStartX + cellWidth * _rows + glyphWidth * 2); | |
//separator line drawing replaced by printing a pipe char | |
// Draw ASCII values | |
addr = lineI * _rows; | |
var asciiVal = new System.Text.StringBuilder(2 + _rows); | |
asciiVal.Append("| "); | |
for (int n = 0; n < _rows && addr < memSize; n++, addr++) | |
{ | |
int c = memData[addr]; | |
asciiVal.Append((c >= 32 && c < 128) ? Convert.ToChar(c) : '.'); | |
} | |
ImGui.TextUnformatted(asciiVal.ToString()); //use unformatted, so string can contain the '%' character | |
} | |
//clipper.End(); //not implemented | |
ImGui.PopStyleVar(2); | |
ImGui.EndChild(); | |
if (dataNext && _dataEditingAddr < memSize) | |
{ | |
_dataEditingAddr = _dataEditingAddr + 1; | |
_dataEditingTakeFocus = true; | |
} | |
ImGui.Separator(); | |
ImGuiNative.igAlignFirstTextHeightToWidgets(); | |
ImGui.PushItemWidth(50); | |
ImGuiNative.igPushAllowKeyboardFocus(false); | |
int rowsBackup = _rows; | |
if (ImGui.DragInt("##rows", ref _rows, 0.2f, 4, 32, "%.0f rows")) | |
{ | |
if (_rows <= 0) _rows = 4; | |
Vector2 newWindowSize = ImGui.GetWindowSize(); | |
newWindowSize.X += (_rows - rowsBackup) * (cellWidth + glyphWidth); | |
ImGui.SetWindowSize(newWindowSize); | |
} | |
ImGuiNative.igPopAllowKeyboardFocus(); | |
ImGui.PopItemWidth(); | |
ImGui.SameLine(); | |
ImGui.Text(string.Format(" Range {0}..{1} ", FixedHex(baseDisplayAddr, addrDigitsCount), | |
FixedHex(baseDisplayAddr + memSize - 1, addrDigitsCount))); | |
ImGui.SameLine(); | |
ImGui.PushItemWidth(70); | |
if (ImGui.InputText("##addr", _addrInput, 32, InputTextFlags.CharsHexadecimal | InputTextFlags.EnterReturnsTrue, null)) | |
{ | |
int gotoAddr; | |
if (TryHexParse(_addrInput, out gotoAddr)) | |
{ | |
gotoAddr -= baseDisplayAddr; | |
if (gotoAddr >= 0 && gotoAddr < memSize) | |
{ | |
ImGui.BeginChild("##scrolling"); | |
ImGuiNative.igSetScrollFromPosY(ImGui.GetCursorStartPos().Y + (gotoAddr / _rows) * ImGuiNative.igGetTextLineHeight()); | |
ImGui.EndChild(); | |
_dataEditingAddr = gotoAddr; | |
_dataEditingTakeFocus = true; | |
} | |
} | |
} | |
ImGui.PopItemWidth(); | |
ImGui.EndWindow(); | |
} | |
} | |
//Not a proper translation, because ImGuiListClipper uses imgui's internal api. | |
//Thus SetCursorPosYAndSetupDummyPrevLine isn't reimplemented, but SetCursorPosY + SetNextWindowContentSize seems to be working well instead. | |
//TODO expose clipper through newer cimgui version | |
internal class ImGuiListClipper | |
{ | |
public float StartPosY; | |
public float ItemsHeight; | |
public int ItemsCount, StepNo, DisplayStart, DisplayEnd; | |
public ImGuiListClipper(int itemsCount = -1, float itemsHeight = -1.0f) | |
{ | |
Begin(itemsCount, itemsHeight); | |
} | |
public void Begin(int count, float itemsHeight = -1.0f) | |
{ | |
StartPosY = ImGuiNative.igGetCursorPosY(); | |
ItemsHeight = itemsHeight; | |
ItemsCount = count; | |
StepNo = 0; | |
DisplayEnd = DisplayStart = -1; | |
if (ItemsHeight > 0.0f) | |
{ | |
ImGui.CalcListClipping(ItemsCount, ItemsHeight, ref DisplayStart, ref DisplayEnd); // calculate how many to clip/display | |
if (DisplayStart > 0) | |
//SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor | |
ImGuiNative.igSetCursorPosY(StartPosY + DisplayStart * ItemsHeight); | |
StepNo = 2; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this!
I forked and updated this to the latest imgui.net nuget package (0.47), btw.