diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs
index a7b70ce35..5710a5991 100644
--- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs
+++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs
@@ -299,12 +299,11 @@ internal sealed partial class Win32InputHandler
private static void ViewportFlagsToWin32Styles(ImGuiViewportFlags flags, out int style, out int exStyle)
{
- style = (flags & ImGuiViewportFlags.NoDecoration) != 0 ? unchecked((int)WS.WS_POPUP) : WS.WS_OVERLAPPEDWINDOW;
- exStyle = (flags & ImGuiViewportFlags.NoTaskBarIcon) != 0 ? WS.WS_EX_TOOLWINDOW : WS.WS_EX_APPWINDOW;
+ style = (int)(flags.HasFlag(ImGuiViewportFlags.NoDecoration) ? WS.WS_POPUP : WS.WS_OVERLAPPEDWINDOW);
+ exStyle =
+ (int)(flags.HasFlag(ImGuiViewportFlags.NoTaskBarIcon) ? WS.WS_EX_TOOLWINDOW : (uint)WS.WS_EX_APPWINDOW);
exStyle |= WS.WS_EX_NOREDIRECTIONBITMAP;
- if ((flags & ImGuiViewportFlags.TopMost) != 0)
+ if (flags.HasFlag(ImGuiViewportFlags.TopMost))
exStyle |= WS.WS_EX_TOPMOST;
- if ((flags & ImGuiViewportFlags.NoInputs) != 0)
- exStyle |= WS.WS_EX_TRANSPARENT | WS.WS_EX_LAYERED;
}
}
diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs
index 0b2e27b57..596df4c67 100644
--- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs
+++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs
@@ -8,7 +8,6 @@ using System.Text;
using Dalamud.Bindings.ImGui;
using Dalamud.Memory;
-using Dalamud.Utility;
using Serilog;
@@ -35,12 +34,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
private readonly HCURSOR[] cursors;
private readonly WndProcDelegate wndProcDelegate;
+ private readonly bool[] imguiMouseIsDown;
private readonly nint platformNamePtr;
private ViewportHandler viewportHandler;
- private int mouseButtonsDown;
- private bool mouseTracked;
private long lastTime;
private nint iniPathPtr;
@@ -66,8 +64,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors |
ImGuiBackendFlags.HasSetMousePos |
ImGuiBackendFlags.RendererHasViewports |
- ImGuiBackendFlags.PlatformHasViewports |
- ImGuiBackendFlags.HasMouseHoveredViewport;
+ ImGuiBackendFlags.PlatformHasViewports;
this.platformNamePtr = Marshal.StringToHGlobalAnsi("imgui_impl_win32_c#");
io.Handle->BackendPlatformName = (byte*)this.platformNamePtr;
@@ -77,6 +74,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable))
this.viewportHandler = new(this);
+ this.imguiMouseIsDown = new bool[5];
+
this.cursors = new HCURSOR[9];
this.cursors[(int)ImGuiMouseCursor.Arrow] = LoadCursorW(default, IDC.IDC_ARROW);
this.cursors[(int)ImGuiMouseCursor.TextInput] = LoadCursorW(default, IDC.IDC_IBEAM);
@@ -96,6 +95,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
private delegate LRESULT WndProcDelegate(HWND hWnd, uint uMsg, WPARAM wparam, LPARAM lparam);
+ private delegate BOOL MonitorEnumProcDelegate(HMONITOR monitor, HDC hdc, RECT* rect, LPARAM lparam);
+
///
public bool UpdateCursor { get; set; } = true;
@@ -154,7 +155,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
public void NewFrame(int targetWidth, int targetHeight)
{
var io = ImGui.GetIO();
- var focusedWindow = GetForegroundWindow();
io.DisplaySize.X = targetWidth;
io.DisplaySize.Y = targetHeight;
@@ -168,9 +168,9 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
this.viewportHandler.UpdateMonitors();
- this.UpdateMouseData(focusedWindow);
+ this.UpdateMousePos();
- this.ProcessKeyEventsWorkarounds(focusedWindow);
+ this.ProcessKeyEventsWorkarounds();
// TODO: need to figure out some way to unify all this
// The bottom case works(?) if the caller hooks SetCursor, but otherwise causes fps issues
@@ -224,40 +224,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
switch (msg)
{
- case WM.WM_MOUSEMOVE:
- {
- if (!this.mouseTracked)
- {
- var tme = new TRACKMOUSEEVENT
- {
- cbSize = (uint)sizeof(TRACKMOUSEEVENT),
- dwFlags = TME.TME_LEAVE,
- hwndTrack = hWndCurrent,
- };
- this.mouseTracked = TrackMouseEvent(&tme);
- }
-
- var mousePos = new POINT(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
- if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
- ClientToScreen(hWndCurrent, &mousePos);
- io.AddMousePosEvent(mousePos.x, mousePos.y);
- break;
- }
-
- case WM.WM_MOUSELEAVE:
- {
- this.mouseTracked = false;
- var mouseScreenPos = new POINT(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
- ClientToScreen(hWndCurrent, &mouseScreenPos);
- if (this.ViewportFromPoint(mouseScreenPos).IsNull)
- {
- var fltMax = ImGuiNative.GETFLTMAX();
- io.AddMousePosEvent(-fltMax, -fltMax);
- }
-
- break;
- }
-
case WM.WM_LBUTTONDOWN:
case WM.WM_LBUTTONDBLCLK:
case WM.WM_RBUTTONDOWN:
@@ -270,10 +236,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
var button = GetButton(msg, wParam);
if (io.WantCaptureMouse)
{
- if (this.mouseButtonsDown == 0 && GetCapture() == nint.Zero)
+ if (!ImGui.IsAnyMouseDown() && GetCapture() == nint.Zero)
SetCapture(hWndCurrent);
- this.mouseButtonsDown |= 1 << button;
- io.AddMouseButtonEvent(button, true);
+
+ io.MouseDown[button] = true;
+ this.imguiMouseIsDown[button] = true;
return default(LRESULT);
}
@@ -289,12 +256,13 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
case WM.WM_XBUTTONUP:
{
var button = GetButton(msg, wParam);
- if (io.WantCaptureMouse)
+ if (io.WantCaptureMouse && this.imguiMouseIsDown[button])
{
- this.mouseButtonsDown &= ~(1 << button);
- if (this.mouseButtonsDown == 0 && GetCapture() == hWndCurrent)
+ if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent)
ReleaseCapture();
- io.AddMouseButtonEvent(button, false);
+
+ io.MouseDown[button] = false;
+ this.imguiMouseIsDown[button] = false;
return default(LRESULT);
}
@@ -304,7 +272,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
case WM.WM_MOUSEWHEEL:
if (io.WantCaptureMouse)
{
- io.AddMouseWheelEvent(0, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
+ io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
return default(LRESULT);
}
@@ -312,7 +280,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
case WM.WM_MOUSEHWHEEL:
if (io.WantCaptureMouse)
{
- io.AddMouseWheelEvent(GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0);
+ io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
return default(LRESULT);
}
@@ -406,84 +374,66 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
this.viewportHandler.UpdateMonitors();
break;
- case WM.WM_SETFOCUS when hWndCurrent == this.hWnd:
- io.AddFocusEvent(true);
- break;
-
case WM.WM_KILLFOCUS when hWndCurrent == this.hWnd:
- io.AddFocusEvent(false);
- // if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent)
- // ReleaseCapture();
- //
- // ImGui.GetIO().WantCaptureMouse = false;
- // ImGui.ClearWindowFocus();
+ if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent)
+ ReleaseCapture();
+
+ ImGui.GetIO().WantCaptureMouse = false;
+ ImGui.ClearWindowFocus();
break;
}
return null;
}
- private void UpdateMouseData(HWND focusedWindow)
+ private void UpdateMousePos()
{
var io = ImGui.GetIO();
+ var pt = default(POINT);
- var mouseScreenPos = default(POINT);
- var hasMouseScreenPos = GetCursorPos(&mouseScreenPos) != 0;
-
- var isAppFocused =
- focusedWindow != default
- && (focusedWindow == this.hWnd
- || IsChild(focusedWindow, this.hWnd)
- || !ImGui.FindViewportByPlatformHandle(focusedWindow).IsNull);
-
- if (isAppFocused)
+ // Depending on if Viewports are enabled, we have to change how we process
+ // the cursor position. If viewports are enabled, we pass the absolute cursor
+ // position to ImGui. Otherwise, we use the old method of passing client-local
+ // mouse position to ImGui.
+ if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable))
{
- // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
- // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions.
if (io.WantSetMousePos)
{
- var pos = new POINT((int)io.MousePos.X, (int)io.MousePos.Y);
- if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
- ClientToScreen(this.hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
+ SetCursorPos((int)io.MousePos.X, (int)io.MousePos.Y);
}
- }
- // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
- if (!io.WantSetMousePos && !this.mouseTracked && hasMouseScreenPos)
- {
- // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
- // (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
- // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
- // (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
- var mousePos = mouseScreenPos;
- if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0)
- ClientToScreen(focusedWindow, &mousePos);
- io.AddMousePosEvent(mousePos.x, mousePos.y);
- }
-
- // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
- // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
- // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that)
- // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
- // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
- // by the backend, and use its flawed heuristic to guess the viewport behind.
- // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
- if (hasMouseScreenPos)
- {
- var viewport = this.ViewportFromPoint(mouseScreenPos);
- io.AddMouseViewportEvent(!viewport.IsNull ? viewport.ID : 0u);
+ if (GetCursorPos(&pt))
+ {
+ io.MousePos.X = pt.x;
+ io.MousePos.Y = pt.y;
+ }
+ else
+ {
+ io.MousePos.X = float.MinValue;
+ io.MousePos.Y = float.MinValue;
+ }
}
else
{
- io.AddMouseViewportEvent(0);
- }
- }
+ if (io.WantSetMousePos)
+ {
+ pt.x = (int)io.MousePos.X;
+ pt.y = (int)io.MousePos.Y;
+ ClientToScreen(this.hWnd, &pt);
+ SetCursorPos(pt.x, pt.y);
+ }
- private ImGuiViewportPtr ViewportFromPoint(POINT mouseScreenPos)
- {
- var hoveredHwnd = WindowFromPoint(mouseScreenPos);
- return hoveredHwnd != default ? ImGui.FindViewportByPlatformHandle(hoveredHwnd) : default;
+ if (GetCursorPos(&pt) && ScreenToClient(this.hWnd, &pt))
+ {
+ io.MousePos.X = pt.x;
+ io.MousePos.Y = pt.y;
+ }
+ else
+ {
+ io.MousePos.X = float.MinValue;
+ io.MousePos.Y = float.MinValue;
+ }
+ }
}
private bool UpdateMouseCursor()
@@ -501,7 +451,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
return true;
}
- private void ProcessKeyEventsWorkarounds(HWND focusedWindow)
+ private void ProcessKeyEventsWorkarounds()
{
// Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one.
if (ImGui.IsKeyDown(ImGuiKey.LeftShift) && !IsVkDown(VK.VK_LSHIFT))
@@ -530,7 +480,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
{
// See: https://github.com/goatcorp/ImGuiScene/pull/13
// > GetForegroundWindow from winuser.h is a surprisingly expensive function.
- var isForeground = focusedWindow == this.hWnd;
+ var isForeground = GetForegroundWindow() == this.hWnd;
for (var i = (int)ImGuiKey.NamedKeyBegin; i < (int)ImGuiKey.NamedKeyEnd; i++)
{
// Skip raising modifier keys if the game is focused.
@@ -696,7 +646,14 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
return;
var pio = ImGui.GetPlatformIO();
- ImGui.GetPlatformIO().Handle->Monitors.Free();
+
+ if (ImGui.GetPlatformIO().Handle->Monitors.Data != null)
+ {
+ // We allocated the platform monitor data in OnUpdateMonitors ourselves,
+ // so we have to free it ourselves to ImGui doesn't try to, or else it will crash
+ Marshal.FreeHGlobal(new IntPtr(ImGui.GetPlatformIO().Handle->Monitors.Data));
+ ImGui.GetPlatformIO().Handle->Monitors = default;
+ }
fixed (char* windowClassNamePtr = WindowClassName)
{
@@ -736,50 +693,59 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
// Here we use a manual ImVector overload, free the existing monitor data,
// and allocate our own, as we are responsible for telling ImGui about monitors
var pio = ImGui.GetPlatformIO();
- pio.Handle->Monitors.Resize(0);
+ var numMonitors = GetSystemMetrics(SM.SM_CMONITORS);
+ var data = Marshal.AllocHGlobal(Marshal.SizeOf() * numMonitors);
+ if (pio.Handle->Monitors.Data != null)
+ Marshal.FreeHGlobal(new IntPtr(pio.Handle->Monitors.Data));
+ pio.Handle->Monitors = new(numMonitors, numMonitors, (ImGuiPlatformMonitor*)data.ToPointer());
- EnumDisplayMonitors(default, null, &EnumDisplayMonitorsCallback, default);
+ // ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
+ // Marshal.FreeHGlobal(platformIO.Handle->Monitors.Data);
+ // int numMonitors = GetSystemMetrics(SystemMetric.SM_CMONITORS);
+ // nint data = Marshal.AllocHGlobal(Marshal.SizeOf() * numMonitors);
+ // platformIO.Handle->Monitors = new ImVector(numMonitors, numMonitors, data);
+
+ var monitorIndex = -1;
+ var enumfn = new MonitorEnumProcDelegate(
+ (hMonitor, _, _, _) =>
+ {
+ monitorIndex++;
+ var info = new MONITORINFO { cbSize = (uint)sizeof(MONITORINFO) };
+ if (!GetMonitorInfoW(hMonitor, &info))
+ return true;
+
+ var monitorLt = new Vector2(info.rcMonitor.left, info.rcMonitor.top);
+ var monitorRb = new Vector2(info.rcMonitor.right, info.rcMonitor.bottom);
+ var workLt = new Vector2(info.rcWork.left, info.rcWork.top);
+ var workRb = new Vector2(info.rcWork.right, info.rcWork.bottom);
+ // Give ImGui the info for this display
+
+ ref var imMonitor = ref ImGui.GetPlatformIO().Monitors.Ref(monitorIndex);
+ imMonitor.MainPos = monitorLt;
+ imMonitor.MainSize = monitorRb - monitorLt;
+ imMonitor.WorkPos = workLt;
+ imMonitor.WorkSize = workRb - workLt;
+ imMonitor.DpiScale = 1f;
+ return true;
+ });
+ EnumDisplayMonitors(
+ default,
+ null,
+ (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(enumfn),
+ default);
Log.Information("Monitors set up!");
- foreach (ref var monitor in pio.Handle->Monitors)
+ for (var i = 0; i < numMonitors; i++)
{
+ var monitor = pio.Handle->Monitors[i];
Log.Information(
- "Monitor: {MainPos} {MainSize} {WorkPos} {WorkSize}",
+ "Monitor {Index}: {MainPos} {MainSize} {WorkPos} {WorkSize}",
+ i,
monitor.MainPos,
monitor.MainSize,
monitor.WorkPos,
monitor.WorkSize);
}
-
- return;
-
- [UnmanagedCallersOnly]
- static BOOL EnumDisplayMonitorsCallback(HMONITOR hMonitor, HDC hdc, RECT* rect, LPARAM lParam)
- {
- var info = new MONITORINFO { cbSize = (uint)sizeof(MONITORINFO) };
- if (!GetMonitorInfoW(hMonitor, &info))
- return true;
-
- var monitorLt = new Vector2(info.rcMonitor.left, info.rcMonitor.top);
- var monitorRb = new Vector2(info.rcMonitor.right, info.rcMonitor.bottom);
- var workLt = new Vector2(info.rcWork.left, info.rcWork.top);
- var workRb = new Vector2(info.rcWork.right, info.rcWork.bottom);
-
- // Give ImGui the info for this display
- var imMonitor = new ImGuiPlatformMonitor
- {
- MainPos = monitorLt,
- MainSize = monitorRb - monitorLt,
- WorkPos = workLt,
- WorkSize = workRb - workLt,
- DpiScale = 1f,
- };
- if ((info.dwFlags & MONITORINFOF_PRIMARY) != 0)
- ImGui.GetPlatformIO().Monitors.PushFront(imMonitor);
- else
- ImGui.GetPlatformIO().Monitors.PushBack(imMonitor);
- return true;
- }
}
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
@@ -828,9 +794,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
null);
}
- if (data->Hwnd == 0)
- Util.Fatal($"CreateWindowExW failed: {GetLastError()}", "ImGui Viewport error");
-
data->HwndOwned = true;
viewport.PlatformRequestResize = false;
viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd;
diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs
index 85ab2e441..ad60d405e 100644
--- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs
+++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs
@@ -15,6 +15,10 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer.Internal;
/// Color stacks to use while evaluating a SeString.
internal sealed class SeStringColorStackSet
{
+ /// Parsed , containing colors to use with and
+ /// .
+ private readonly uint[,] colorTypes;
+
/// Foreground color stack while evaluating a SeString for rendering.
/// Touched only from the main thread.
private readonly List colorStack = [];
@@ -35,38 +39,30 @@ internal sealed class SeStringColorStackSet
foreach (var row in uiColor)
maxId = (int)Math.Max(row.RowId, maxId);
- this.ColorTypes = new uint[maxId + 1, 4];
+ this.colorTypes = new uint[maxId + 1, 4];
foreach (var row in uiColor)
{
// Contains ABGR.
- this.ColorTypes[row.RowId, 0] = row.Dark;
- this.ColorTypes[row.RowId, 1] = row.Light;
- this.ColorTypes[row.RowId, 2] = row.ClassicFF;
- this.ColorTypes[row.RowId, 3] = row.ClearBlue;
+ this.colorTypes[row.RowId, 0] = row.Dark;
+ this.colorTypes[row.RowId, 1] = row.Light;
+ this.colorTypes[row.RowId, 2] = row.ClassicFF;
+ this.colorTypes[row.RowId, 3] = row.ClearBlue;
}
if (BitConverter.IsLittleEndian)
{
// ImGui wants RGBA in LE.
- fixed (uint* p = this.ColorTypes)
+ fixed (uint* p = this.colorTypes)
{
- foreach (ref var r in new Span(p, this.ColorTypes.GetLength(0) * this.ColorTypes.GetLength(1)))
+ foreach (ref var r in new Span(p, this.colorTypes.GetLength(0) * this.colorTypes.GetLength(1)))
r = BinaryPrimitives.ReverseEndianness(r);
}
}
}
- /// Initializes a new instance of the class.
- /// Color types.
- public SeStringColorStackSet(uint[,] colorTypes) => this.ColorTypes = colorTypes;
-
/// Gets a value indicating whether at least one color has been pushed to the edge color stack.
public bool HasAdditionalEdgeColor { get; private set; }
- /// Gets the parsed containing colors to use with
- /// and .
- public uint[,] ColorTypes { get; }
-
/// Resets the colors in the stack.
/// Draw state.
internal void Initialize(scoped ref SeStringDrawState drawState)
@@ -195,9 +191,9 @@ internal sealed class SeStringColorStackSet
}
// Opacity component is ignored.
- var color = themeIndex >= 0 && themeIndex < this.ColorTypes.GetLength(1) &&
- colorTypeIndex < this.ColorTypes.GetLength(0)
- ? this.ColorTypes[colorTypeIndex, themeIndex]
+ var color = themeIndex >= 0 && themeIndex < this.colorTypes.GetLength(1) &&
+ colorTypeIndex < this.colorTypes.GetLength(0)
+ ? this.colorTypes[colorTypeIndex, themeIndex]
: 0u;
rgbaStack.Add(color | 0xFF000000u);
diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs
index 87df2da2c..d0c40cd9f 100644
--- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs
+++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Numerics;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@@ -26,7 +25,7 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer.Internal;
/// Draws SeString.
[ServiceManager.EarlyLoadedService]
-internal class SeStringRenderer : IServiceType
+internal unsafe class SeStringRenderer : IInternalDisposableService
{
private const int ImGuiContextCurrentWindowOffset = 0x3FF0;
private const int ImGuiWindowDcOffset = 0x118;
@@ -48,19 +47,28 @@ internal class SeStringRenderer : IServiceType
/// Parsed text fragments from a SeString.
/// Touched only from the main thread.
- private readonly List fragmentsMainThread = [];
+ private readonly List fragments = [];
/// Color stacks to use while evaluating a SeString for rendering.
/// Touched only from the main thread.
- private readonly SeStringColorStackSet colorStackSetMainThread;
+ private readonly SeStringColorStackSet colorStackSet;
+
+ /// Splits a draw list so that different layers of a single glyph can be drawn out of order.
+ private ImDrawListSplitter* splitter = ImGui.ImDrawListSplitter();
[ServiceManager.ServiceConstructor]
private SeStringRenderer(DataManager dm, TargetSigScanner sigScanner)
{
- this.colorStackSetMainThread = new(dm.Excel.GetSheet());
+ this.colorStackSet = new(dm.Excel.GetSheet());
this.gfd = dm.GetFile("common/font/gfdata.gfd")!;
}
+ /// Finalizes an instance of the class.
+ ~SeStringRenderer() => this.ReleaseUnmanagedResources();
+
+ ///
+ void IInternalDisposableService.DisposeService() => this.ReleaseUnmanagedResources();
+
/// Compiles and caches a SeString from a text macro representation.
/// SeString text macro representation.
/// Newline characters will be normalized to newline payloads.
@@ -72,44 +80,6 @@ internal class SeStringRenderer : IServiceType
text.ReplaceLineEndings("
"),
new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }));
- /// Creates a draw data that will draw the given SeString onto it.
- /// SeString to render.
- /// Parameters for drawing.
- /// A new self-contained draw data.
- public unsafe BufferBackedImDrawData CreateDrawData(
- ReadOnlySeStringSpan sss,
- scoped in SeStringDrawParams drawParams = default)
- {
- if (drawParams.TargetDrawList is not null)
- {
- throw new ArgumentException(
- $"{nameof(SeStringDrawParams.TargetDrawList)} may not be specified.",
- nameof(drawParams));
- }
-
- var dd = BufferBackedImDrawData.Create();
-
- try
- {
- var size = this.Draw(sss, drawParams with { TargetDrawList = dd.ListPtr }).Size;
-
- var offset = drawParams.ScreenOffset ?? Vector2.Zero;
- foreach (var vtx in new Span(dd.ListPtr.VtxBuffer.Data, dd.ListPtr.VtxBuffer.Size))
- offset = Vector2.Min(offset, vtx.Pos);
-
- dd.Data.DisplayPos = offset;
- dd.Data.DisplaySize = size - offset;
- dd.Data.Valid = 1;
- dd.UpdateDrawDataStatistics();
- return dd;
- }
- catch
- {
- dd.Dispose();
- throw;
- }
- }
-
/// Compiles and caches a SeString from a text macro representation, and then draws it.
/// SeString text macro representation.
/// Newline characters will be normalized to newline payloads.
@@ -143,42 +113,28 @@ internal class SeStringRenderer : IServiceType
/// ImGui ID, if link functionality is desired.
/// Button flags to use on link interaction.
/// Interaction result of the rendered text.
- public unsafe SeStringDrawResult Draw(
+ public SeStringDrawResult Draw(
ReadOnlySeStringSpan sss,
scoped in SeStringDrawParams drawParams = default,
ImGuiId imGuiId = default,
ImGuiButtonFlags buttonFlags = ImGuiButtonFlags.MouseButtonDefault)
{
- // Interactivity is supported only from the main thread.
- if (!imGuiId.IsEmpty())
- ThreadSafety.AssertMainThread();
+ // Drawing is only valid if done from the main thread anyway, especially with interactivity.
+ ThreadSafety.AssertMainThread();
if (drawParams.TargetDrawList is not null && imGuiId)
throw new ArgumentException("ImGuiId cannot be set if TargetDrawList is manually set.", nameof(imGuiId));
- using var cleanup = new DisposeSafety.ScopedFinalizer();
-
- ImFont* font = null;
- if (drawParams.Font.HasValue)
- font = drawParams.Font.Value;
- if (ThreadSafety.IsMainThread && drawParams.TargetDrawList is null && font is null)
- font = ImGui.GetFont();
- if (font is null)
- throw new ArgumentException("Specified font is empty.");
-
// This also does argument validation for drawParams. Do it here.
- // `using var` makes a struct read-only, but we do want to modify it.
- var stateStorage = new SeStringDrawState(
- sss,
- drawParams,
- ThreadSafety.IsMainThread ? this.colorStackSetMainThread : new(this.colorStackSetMainThread.ColorTypes),
- ThreadSafety.IsMainThread ? this.fragmentsMainThread : [],
- font);
- ref var state = ref Unsafe.AsRef(in stateStorage);
+ var state = new SeStringDrawState(sss, drawParams, this.colorStackSet, this.splitter);
+
+ // Reset and initialize the state.
+ this.fragments.Clear();
+ this.colorStackSet.Initialize(ref state);
// Analyze the provided SeString and break it up to text fragments.
this.CreateTextFragments(ref state);
- var fragmentSpan = CollectionsMarshal.AsSpan(state.Fragments);
+ var fragmentSpan = CollectionsMarshal.AsSpan(this.fragments);
// Calculate size.
var size = Vector2.Zero;
@@ -191,19 +147,26 @@ internal class SeStringRenderer : IServiceType
state.SplitDrawList();
- var itemSize = size;
- if (drawParams.TargetDrawList is null)
+ // Handle cases where ImGui.AlignTextToFramePadding has been called.
+ var context = ImGui.GetCurrentContext();
+ var currLineTextBaseOffset = 0f;
+ if (!context.IsNull)
{
- // Handle cases where ImGui.AlignTextToFramePadding has been called.
- var currLineTextBaseOffset = ImGui.GetCurrentContext().CurrentWindow.DC.CurrLineTextBaseOffset;
- if (currLineTextBaseOffset != 0f)
+ var currentWindow = context.CurrentWindow;
+ if (!currentWindow.IsNull)
{
- itemSize.Y += 2 * currLineTextBaseOffset;
- foreach (ref var f in fragmentSpan)
- f.Offset += new Vector2(0, currLineTextBaseOffset);
+ currLineTextBaseOffset = currentWindow.DC.CurrLineTextBaseOffset;
}
}
+ var itemSize = size;
+ if (currLineTextBaseOffset != 0f)
+ {
+ itemSize.Y += 2 * currLineTextBaseOffset;
+ foreach (ref var f in fragmentSpan)
+ f.Offset += new Vector2(0, currLineTextBaseOffset);
+ }
+
// Draw all text fragments.
var lastRune = default(Rune);
foreach (ref var f in fragmentSpan)
@@ -317,6 +280,15 @@ internal class SeStringRenderer : IServiceType
return displayRune.Value != 0;
}
+ private void ReleaseUnmanagedResources()
+ {
+ if (this.splitter is not null)
+ {
+ this.splitter->Destroy();
+ this.splitter = null;
+ }
+ }
+
/// Creates text fragment, taking line and word breaking into account.
/// Draw state.
private void CreateTextFragments(ref SeStringDrawState state)
@@ -419,7 +391,7 @@ internal class SeStringRenderer : IServiceType
var overflows = Math.Max(w, xy.X + fragment.VisibleWidth) > state.WrapWidth;
// Test if the fragment does not fit into the current line and the current line is not empty.
- if (xy.X != 0 && state.Fragments.Count > 0 && !state.Fragments[^1].BreakAfter && overflows)
+ if (xy.X != 0 && this.fragments.Count > 0 && !this.fragments[^1].BreakAfter && overflows)
{
// Introduce break if this is the first time testing the current break unit or the current fragment
// is an entity.
@@ -429,7 +401,7 @@ internal class SeStringRenderer : IServiceType
xy.X = 0;
xy.Y += state.LineHeight;
w = 0;
- CollectionsMarshal.AsSpan(state.Fragments)[^1].BreakAfter = true;
+ CollectionsMarshal.AsSpan(this.fragments)[^1].BreakAfter = true;
fragment.Offset = xy;
// Now that the fragment is given its own line, test if it overflows again.
@@ -447,16 +419,16 @@ internal class SeStringRenderer : IServiceType
fragment = this.CreateFragment(state, prev, curr, true, xy, link, entity, remainingWidth);
}
}
- else if (state.Fragments.Count > 0 && xy.X != 0)
+ else if (this.fragments.Count > 0 && xy.X != 0)
{
// New fragment fits into the current line, and it has a previous fragment in the same line.
// If the previous fragment ends with a soft hyphen, adjust its width so that the width of its
// trailing soft hyphens are not considered.
- if (state.Fragments[^1].EndsWithSoftHyphen)
- xy.X += state.Fragments[^1].AdvanceWidthWithoutSoftHyphen - state.Fragments[^1].AdvanceWidth;
+ if (this.fragments[^1].EndsWithSoftHyphen)
+ xy.X += this.fragments[^1].AdvanceWidthWithoutSoftHyphen - this.fragments[^1].AdvanceWidth;
// Adjust this fragment's offset from kerning distance.
- xy.X += state.CalculateScaledDistance(state.Fragments[^1].LastRune, fragment.FirstRune);
+ xy.X += state.CalculateScaledDistance(this.fragments[^1].LastRune, fragment.FirstRune);
fragment.Offset = xy;
}
@@ -467,7 +439,7 @@ internal class SeStringRenderer : IServiceType
w = Math.Max(w, xy.X + fragment.VisibleWidth);
xy.X += fragment.AdvanceWidth;
prev = fragment.To;
- state.Fragments.Add(fragment);
+ this.fragments.Add(fragment);
if (fragment.BreakAfter)
{
@@ -519,7 +491,7 @@ internal class SeStringRenderer : IServiceType
if (gfdTextureSrv != 0)
{
state.Draw(
- new(gfdTextureSrv),
+ new ImTextureID(gfdTextureSrv),
offset + new Vector2(x, MathF.Round((state.LineHeight - size.Y) / 2)),
size,
useHq ? gfdEntry.HqUv0 : gfdEntry.Uv0,
@@ -556,7 +528,7 @@ internal class SeStringRenderer : IServiceType
return;
- static unsafe nint GetGfdTextureSrv()
+ static nint GetGfdTextureSrv()
{
var uim = UIModule.Instance();
if (uim is null)
@@ -581,7 +553,7 @@ internal class SeStringRenderer : IServiceType
/// Determines a bitmap icon to display for the given SeString payload.
/// Byte span that should include a SeString payload.
/// Icon to display, or if it should not be displayed as an icon.
- private unsafe BitmapFontIcon GetBitmapFontIconFor(ReadOnlySpan sss)
+ private BitmapFontIcon GetBitmapFontIconFor(ReadOnlySpan sss)
{
var e = new ReadOnlySeStringSpan(sss).GetEnumerator();
if (!e.MoveNext() || e.Current.MacroCode is not MacroCode.Icon and not MacroCode.Icon2)
@@ -738,4 +710,38 @@ internal class SeStringRenderer : IServiceType
firstDisplayRune ?? default,
lastNonSoftHyphenRune);
}
+
+ /// Represents a text fragment in a SeString span.
+ /// Starting byte offset (inclusive) in a SeString.
+ /// Ending byte offset (exclusive) in a SeString.
+ /// Byte offset of the link that decorates this text fragment, or -1 if none.
+ /// Offset in pixels w.r.t. .
+ /// Replacement entity, if any.
+ /// Visible width of this text fragment. This is the width required to draw everything
+ /// without clipping.
+ /// Advance width of this text fragment. This is the width required to add to the cursor
+ /// to position the next fragment correctly.
+ /// Same with , but trimming all the
+ /// trailing soft hyphens.
+ /// Whether to insert a line break after this text fragment.
+ /// Whether this text fragment ends with one or more soft hyphens.
+ /// First rune in this text fragment.
+ /// Last rune in this text fragment, for the purpose of calculating kerning distance with
+ /// the following text fragment in the same line, if any.
+ private record struct TextFragment(
+ int From,
+ int To,
+ int Link,
+ Vector2 Offset,
+ SeStringReplacementEntity Entity,
+ float VisibleWidth,
+ float AdvanceWidth,
+ float AdvanceWidthWithoutSoftHyphen,
+ bool BreakAfter,
+ bool EndsWithSoftHyphen,
+ Rune FirstRune,
+ Rune LastRune)
+ {
+ public bool IsSoftHyphenVisible => this.EndsWithSoftHyphen && this.BreakAfter;
+ }
}
diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/TextFragment.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/TextFragment.cs
deleted file mode 100644
index a64c32109..000000000
--- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/TextFragment.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Numerics;
-using System.Text;
-
-namespace Dalamud.Interface.ImGuiSeStringRenderer.Internal;
-
-/// Represents a text fragment in a SeString span.
-/// Starting byte offset (inclusive) in a SeString.
-/// Ending byte offset (exclusive) in a SeString.
-/// Byte offset of the link that decorates this text fragment, or -1 if none.
-/// Offset in pixels w.r.t. .
-/// Replacement entity, if any.
-/// Visible width of this text fragment. This is the width required to draw everything
-/// without clipping.
-/// Advance width of this text fragment. This is the width required to add to the cursor
-/// to position the next fragment correctly.
-/// Same with , but trimming all the
-/// trailing soft hyphens.
-/// Whether to insert a line break after this text fragment.
-/// Whether this text fragment ends with one or more soft hyphens.
-/// First rune in this text fragment.
-/// Last rune in this text fragment, for the purpose of calculating kerning distance with
-/// the following text fragment in the same line, if any.
-internal record struct TextFragment(
- int From,
- int To,
- int Link,
- Vector2 Offset,
- SeStringReplacementEntity Entity,
- float VisibleWidth,
- float AdvanceWidth,
- float AdvanceWidthWithoutSoftHyphen,
- bool BreakAfter,
- bool EndsWithSoftHyphen,
- Rune FirstRune,
- Rune LastRune)
-{
- /// Gets a value indicating whether the fragment ends with a visible soft hyphen.
- public bool IsSoftHyphenVisible => this.EndsWithSoftHyphen && this.BreakAfter;
-}
diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs
index 11c1120b4..64a7f3db3 100644
--- a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs
+++ b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -7,8 +6,6 @@ using System.Text;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
using Dalamud.Interface.Utility;
-using Dalamud.Utility;
-
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
@@ -22,75 +19,46 @@ public unsafe ref struct SeStringDrawState
private static readonly int ChannelCount = Enum.GetValues().Length;
private readonly ImDrawList* drawList;
-
- private ImDrawListSplitter splitter;
+ private readonly SeStringColorStackSet colorStackSet;
+ private readonly ImDrawListSplitter* splitter;
/// Initializes a new instance of the struct.
/// Raw SeString byte span.
/// Instance of to initialize from.
/// Instance of to use.
- /// Fragments.
- /// Font to use.
+ /// Instance of ImGui Splitter to use.
internal SeStringDrawState(
ReadOnlySpan span,
scoped in SeStringDrawParams ssdp,
SeStringColorStackSet colorStackSet,
- List fragments,
- ImFont* font)
+ ImDrawListSplitter* splitter)
{
+ this.colorStackSet = colorStackSet;
+ this.splitter = splitter;
+ this.drawList = ssdp.TargetDrawList ?? ImGui.GetWindowDrawList();
this.Span = span;
- this.ColorStackSet = colorStackSet;
- this.Fragments = fragments;
- this.Font = font;
-
- if (ssdp.TargetDrawList is null)
- {
- if (!ThreadSafety.IsMainThread)
- {
- throw new ArgumentException(
- $"{nameof(ssdp.TargetDrawList)} must be set to render outside the main thread.");
- }
-
- this.drawList = ssdp.TargetDrawList ?? ImGui.GetWindowDrawList();
- this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos();
- this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize();
- this.WrapWidth = ssdp.WrapWidth ?? ImGui.GetContentRegionAvail().X;
- this.Color = ssdp.Color ?? ImGui.GetColorU32(ImGuiCol.Text);
- this.LinkHoverBackColor = ssdp.LinkHoverBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonHovered);
- this.LinkActiveBackColor = ssdp.LinkActiveBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonActive);
- this.ThemeIndex = ssdp.ThemeIndex ?? AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType;
- }
- else
- {
- this.drawList = ssdp.TargetDrawList.Value;
- this.ScreenOffset = Vector2.Zero;
- this.FontSize = ssdp.FontSize ?? throw new ArgumentException(
- $"{nameof(ssdp.FontSize)} must be set to render outside the main thread.");
- this.WrapWidth = ssdp.WrapWidth ?? float.MaxValue;
- this.Color = ssdp.Color ?? uint.MaxValue;
- this.LinkHoverBackColor = 0; // Interactivity is unused outside the main thread.
- this.LinkActiveBackColor = 0; // Interactivity is unused outside the main thread.
- this.ThemeIndex = ssdp.ThemeIndex ?? 0;
- }
-
- this.splitter = default;
this.GetEntity = ssdp.GetEntity;
+ this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos();
this.ScreenOffset = new(MathF.Round(this.ScreenOffset.X), MathF.Round(this.ScreenOffset.Y));
+ this.Font = ssdp.EffectiveFont;
+ this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize();
this.FontSizeScale = this.FontSize / this.Font->FontSize;
this.LineHeight = MathF.Round(ssdp.EffectiveLineHeight);
+ this.WrapWidth = ssdp.WrapWidth ?? ImGui.GetContentRegionAvail().X;
this.LinkUnderlineThickness = ssdp.LinkUnderlineThickness ?? 0f;
this.Opacity = ssdp.EffectiveOpacity;
this.EdgeOpacity = (ssdp.EdgeStrength ?? 0.25f) * ssdp.EffectiveOpacity;
+ this.Color = ssdp.Color ?? ImGui.GetColorU32(ImGuiCol.Text);
this.EdgeColor = ssdp.EdgeColor ?? 0xFF000000;
this.ShadowColor = ssdp.ShadowColor ?? 0xFF000000;
+ this.LinkHoverBackColor = ssdp.LinkHoverBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonHovered);
+ this.LinkActiveBackColor = ssdp.LinkActiveBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonActive);
this.ForceEdgeColor = ssdp.ForceEdgeColor;
+ this.ThemeIndex = ssdp.ThemeIndex ?? AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType;
this.Bold = ssdp.Bold;
this.Italic = ssdp.Italic;
this.Edge = ssdp.Edge;
this.Shadow = ssdp.Shadow;
-
- this.ColorStackSet.Initialize(ref this);
- fragments.Clear();
}
///
@@ -167,7 +135,7 @@ public unsafe ref struct SeStringDrawState
/// Gets a value indicating whether the edge should be drawn.
public readonly bool ShouldDrawEdge =>
- (this.Edge || this.ColorStackSet.HasAdditionalEdgeColor) && this.EdgeColor >= 0x1000000;
+ (this.Edge || this.colorStackSet.HasAdditionalEdgeColor) && this.EdgeColor >= 0x1000000;
/// Gets a value indicating whether the edge should be drawn.
public readonly bool ShouldDrawShadow => this is { Shadow: true, ShadowColor: >= 0x1000000 };
@@ -175,17 +143,11 @@ public unsafe ref struct SeStringDrawState
/// Gets a value indicating whether the edge should be drawn.
public readonly bool ShouldDrawForeground => this is { Color: >= 0x1000000 };
- /// Gets the color stacks.
- internal SeStringColorStackSet ColorStackSet { get; }
-
- /// Gets the text fragments.
- internal List Fragments { get; }
-
/// Sets the current channel in the ImGui draw list splitter.
/// Channel to switch to.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void SetCurrentChannel(SeStringDrawChannel channelIndex) =>
- this.splitter.SetCurrentChannel(this.drawList, (int)channelIndex);
+ public readonly void SetCurrentChannel(SeStringDrawChannel channelIndex) =>
+ this.splitter->SetCurrentChannel(this.drawList, (int)channelIndex);
/// Draws a single texture.
/// ImGui texture ID to draw from.
@@ -254,7 +216,7 @@ public unsafe ref struct SeStringDrawState
/// Draws a single glyph using current styling configurations.
/// Glyph to draw.
/// Offset of the glyph in pixels w.r.t. .
- internal void DrawGlyph(scoped in ImGuiHelpers.ImFontGlyphReal g, Vector2 offset)
+ internal readonly void DrawGlyph(scoped in ImGuiHelpers.ImFontGlyphReal g, Vector2 offset)
{
var texId = this.Font->ContainerAtlas->Textures.Ref(g.TextureIndex).TexID;
var xy0 = new Vector2(
@@ -306,7 +268,7 @@ public unsafe ref struct SeStringDrawState
/// Offset of the glyph in pixels w.r.t.
/// .
/// Advance width of the glyph.
- internal void DrawLinkUnderline(Vector2 offset, float advanceWidth)
+ internal readonly void DrawLinkUnderline(Vector2 offset, float advanceWidth)
{
if (this.LinkUnderlineThickness < 1f)
return;
@@ -388,15 +350,15 @@ public unsafe ref struct SeStringDrawState
switch (payload.MacroCode)
{
case MacroCode.Color:
- this.ColorStackSet.HandleColorPayload(ref this, payload);
+ this.colorStackSet.HandleColorPayload(ref this, payload);
return true;
case MacroCode.EdgeColor:
- this.ColorStackSet.HandleEdgeColorPayload(ref this, payload);
+ this.colorStackSet.HandleEdgeColorPayload(ref this, payload);
return true;
case MacroCode.ShadowColor:
- this.ColorStackSet.HandleShadowColorPayload(ref this, payload);
+ this.colorStackSet.HandleShadowColorPayload(ref this, payload);
return true;
case MacroCode.Bold when payload.TryGetExpression(out var e) && e.TryGetUInt(out var u):
@@ -417,11 +379,11 @@ public unsafe ref struct SeStringDrawState
return true;
case MacroCode.ColorType:
- this.ColorStackSet.HandleColorTypePayload(ref this, payload);
+ this.colorStackSet.HandleColorTypePayload(ref this, payload);
return true;
case MacroCode.EdgeColorType:
- this.ColorStackSet.HandleEdgeColorTypePayload(ref this, payload);
+ this.colorStackSet.HandleEdgeColorTypePayload(ref this, payload);
return true;
default:
@@ -431,9 +393,10 @@ public unsafe ref struct SeStringDrawState
/// Splits the draw list.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void SplitDrawList() => this.splitter.Split(this.drawList, ChannelCount);
+ internal readonly void SplitDrawList() =>
+ this.splitter->Split(this.drawList, ChannelCount);
/// Merges the draw list.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void MergeDrawList() => this.splitter.Merge(this.drawList);
+ internal readonly void MergeDrawList() => this.splitter->Merge(this.drawList);
}
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index af78c5b0c..64e1acaa4 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -533,13 +533,6 @@ internal class DalamudInterface : IInternalDisposableService
this.creditsDarkeningAnimation.Restart();
}
- ///
- public T GetDataWindowWidget() where T : IDataWindowWidget => this.dataWindow.GetWidget();
-
- /// Sets the data window current widget.
- /// Widget to set current.
- public void SetDataWindowWidget(IDataWindowWidget widget) => this.dataWindow.CurrentWidget = widget;
-
private void OnDraw()
{
this.FrameCount++;
diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
index eb0589d59..ae86958dd 100644
--- a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
@@ -68,7 +68,7 @@ internal class DataWindow : Window, IDisposable
private bool isExcept;
private bool selectionCollapsed;
-
+ private IDataWindowWidget currentWidget;
private bool isLoaded;
///
@@ -82,12 +82,9 @@ internal class DataWindow : Window, IDisposable
this.RespectCloseHotkey = false;
this.orderedModules = this.modules.OrderBy(module => module.DisplayName);
- this.CurrentWidget = this.orderedModules.First();
+ this.currentWidget = this.orderedModules.First();
}
- /// Gets or sets the current widget.
- public IDataWindowWidget CurrentWidget { get; set; }
-
///
public void Dispose() => this.modules.OfType().AggregateToDisposable().Dispose();
@@ -102,20 +99,6 @@ internal class DataWindow : Window, IDisposable
{
}
- /// Gets the data window widget of the specified type.
- /// Type of the data window widget to find.
- /// Found widget.
- public T GetWidget() where T : IDataWindowWidget
- {
- foreach (var m in this.modules)
- {
- if (m is T w)
- return w;
- }
-
- throw new ArgumentException($"No widget of type {typeof(T).FullName} found.");
- }
-
///
/// Set the DataKind dropdown menu.
///
@@ -127,7 +110,7 @@ internal class DataWindow : Window, IDisposable
if (this.modules.FirstOrDefault(module => module.IsWidgetCommand(dataKind)) is { } targetModule)
{
- this.CurrentWidget = targetModule;
+ this.currentWidget = targetModule;
}
else
{
@@ -170,9 +153,9 @@ internal class DataWindow : Window, IDisposable
{
foreach (var widget in this.orderedModules)
{
- if (ImGui.Selectable(widget.DisplayName, this.CurrentWidget == widget))
+ if (ImGui.Selectable(widget.DisplayName, this.currentWidget == widget))
{
- this.CurrentWidget = widget;
+ this.currentWidget = widget;
}
}
@@ -223,9 +206,9 @@ internal class DataWindow : Window, IDisposable
try
{
- if (this.CurrentWidget is { Ready: true })
+ if (this.currentWidget is { Ready: true })
{
- this.CurrentWidget.Draw();
+ this.currentWidget.Draw();
}
else
{
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs
index 4f5540daf..91f1af98e 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs
@@ -1,15 +1,9 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Numerics;
-using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
-using Dalamud.Interface.Components;
-using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Utility;
-using Dalamud.Interface.Utility.Internal;
-
-using Lumina.Text.ReadOnly;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@@ -93,30 +87,12 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
ImGuiHelpers.ScaledDummy(10f);
for (var i = 0; i < this.icons?.Count; i++)
{
- if (this.icons[i] == FontAwesomeIcon.None)
- continue;
-
- ImGui.AlignTextToFramePadding();
ImGui.Text($"0x{(int)this.icons[i].ToIconChar():X}");
ImGuiHelpers.ScaledRelativeSameLine(50f);
ImGui.Text($"{this.iconNames?[i]}");
ImGuiHelpers.ScaledRelativeSameLine(280f);
ImGui.PushFont(this.useFixedWidth ? InterfaceManager.IconFontFixedWidth : InterfaceManager.IconFont);
ImGui.Text(this.icons[i].ToIconString());
- ImGuiHelpers.ScaledRelativeSameLine(320f);
- if (this.useFixedWidth
- ? ImGui.Button($"{(char)this.icons[i]}##FontAwesomeIconButton{i}")
- : ImGuiComponents.IconButton($"##FontAwesomeIconButton{i}", this.icons[i]))
- {
- _ = Service.Get().ShowTextureSaveMenuAsync(
- this.DisplayName,
- this.icons[i].ToString(),
- Task.FromResult(
- Service.Get().CreateTextureFromSeString(
- ReadOnlySeString.FromText(this.icons[i].ToIconString()),
- new() { Font = ImGui.GetFont(), FontSize = ImGui.GetFontSize() })));
- }
-
ImGui.PopFont();
ImGuiHelpers.ScaledDummy(2f);
}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs
index 0f51e0322..7ff5a63be 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs
@@ -1,6 +1,5 @@
using System.Numerics;
using System.Text;
-using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
using Dalamud.Data;
@@ -10,13 +9,11 @@ using Dalamud.Interface.ImGuiSeStringRenderer;
using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Utility;
-using Dalamud.Interface.Utility.Internal;
using Dalamud.Storage.Assets;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Excel.Sheets;
using Lumina.Text;
-using Lumina.Text.Parse;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
@@ -59,11 +56,11 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
///
public void Draw()
{
- var t2 = ImGui.ColorConvertU32ToFloat4(this.style.Color ??= ImGui.GetColorU32(ImGuiCol.Text));
+ var t2 = ImGui.ColorConvertU32ToFloat4(this.style.Color ?? ImGui.GetColorU32(ImGuiCol.Text));
if (ImGui.ColorEdit4("Color", ref t2))
this.style.Color = ImGui.ColorConvertFloat4ToU32(t2);
- t2 = ImGui.ColorConvertU32ToFloat4(this.style.EdgeColor ??= 0xFF000000u);
+ t2 = ImGui.ColorConvertU32ToFloat4(this.style.EdgeColor ?? 0xFF000000u);
if (ImGui.ColorEdit4("Edge Color", ref t2))
this.style.EdgeColor = ImGui.ColorConvertFloat4ToU32(t2);
@@ -72,27 +69,27 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
if (ImGui.Checkbox("Forced"u8, ref t))
this.style.ForceEdgeColor = t;
- t2 = ImGui.ColorConvertU32ToFloat4(this.style.ShadowColor ??= 0xFF000000u);
- if (ImGui.ColorEdit4("Shadow Color"u8, ref t2))
+ t2 = ImGui.ColorConvertU32ToFloat4(this.style.ShadowColor ?? 0xFF000000u);
+ if (ImGui.ColorEdit4("Shadow Color", ref t2))
this.style.ShadowColor = ImGui.ColorConvertFloat4ToU32(t2);
- t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkHoverBackColor ??= ImGui.GetColorU32(ImGuiCol.ButtonHovered));
- if (ImGui.ColorEdit4("Link Hover Color"u8, ref t2))
+ t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkHoverBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonHovered));
+ if (ImGui.ColorEdit4("Link Hover Color", ref t2))
this.style.LinkHoverBackColor = ImGui.ColorConvertFloat4ToU32(t2);
- t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkActiveBackColor ??= ImGui.GetColorU32(ImGuiCol.ButtonActive));
- if (ImGui.ColorEdit4("Link Active Color"u8, ref t2))
+ t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkActiveBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonActive));
+ if (ImGui.ColorEdit4("Link Active Color", ref t2))
this.style.LinkActiveBackColor = ImGui.ColorConvertFloat4ToU32(t2);
- var t3 = this.style.LineHeight ??= 1f;
+ var t3 = this.style.LineHeight ?? 1f;
if (ImGui.DragFloat("Line Height"u8, ref t3, 0.01f, 0.4f, 3f, "%.02f"))
this.style.LineHeight = t3;
- t3 = this.style.Opacity ??= 1f;
+ t3 = this.style.Opacity ?? ImGui.GetStyle().Alpha;
if (ImGui.DragFloat("Opacity"u8, ref t3, 0.005f, 0f, 1f, "%.02f"))
this.style.Opacity = t3;
- t3 = this.style.EdgeStrength ??= 0.25f;
+ t3 = this.style.EdgeStrength ?? 0.25f;
if (ImGui.DragFloat("Edge Strength"u8, ref t3, 0.005f, 0f, 1f, "%.02f"))
this.style.EdgeStrength = t3;
@@ -243,27 +240,6 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
Service.Get().CompileAndCache(this.testString).Data.Span));
}
- ImGui.SameLine();
-
- if (ImGui.Button("Copy as Image"))
- {
- _ = Service.Get().ShowTextureSaveMenuAsync(
- this.DisplayName,
- $"From {nameof(SeStringRendererTestWidget)}",
- Task.FromResult(
- Service.Get().CreateTextureFromSeString(
- ReadOnlySeString.FromMacroString(
- this.testString,
- new(ExceptionMode: MacroStringParseExceptionMode.EmbedError)),
- this.style with
- {
- Font = ImGui.GetFont(),
- FontSize = ImGui.GetFontSize(),
- WrapWidth = ImGui.GetContentRegionAvail().X,
- ThemeIndex = AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType,
- })));
- }
-
ImGuiHelpers.ScaledDummy(3);
ImGuiHelpers.CompileSeStringWrapped(
"Optional features implemented for the following test input:
" +
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs
index 3416a2506..52fa0e822 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
@@ -306,12 +306,12 @@ internal class TexWidget : IDataWindowWidget
pres->Release();
ImGui.Text($"RC: Resource({rcres})/View({rcsrv})");
- ImGui.Text($"{source.Width} x {source.Height} | {source}");
+ ImGui.Text(source.ToString());
}
else
{
- ImGui.Text("RC: -");
- ImGui.Text(string.Empty);
+ ImGui.Text("RC: -"u8);
+ ImGui.Text(" "u8);
}
}
@@ -342,10 +342,6 @@ internal class TexWidget : IDataWindowWidget
runLater?.Invoke();
}
- /// Adds a texture wrap for debug display purposes.
- /// Task returning a texture.
- public void AddTexture(Task textureTask) => this.addedTextures.Add(new(Api10: textureTask));
-
private unsafe void DrawBlame(List allBlames)
{
var im = Service.Get();
diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
index 69cdc4d28..e3eb22a04 100644
--- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
@@ -86,8 +86,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
: base(
"TitleScreenMenuOverlay",
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar |
- ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus |
- ImGuiWindowFlags.NoDocking)
+ ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus)
{
this.showTsm = consoleManager.AddVariable("dalamud.show_tsm", "Show the Title Screen Menu", true);
diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs
index be2f5a742..2853aa4d2 100644
--- a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs
@@ -1,5 +1,4 @@
-using System.Threading;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
@@ -34,22 +33,10 @@ public interface IFontHandle : IDisposable
///
///
/// Use directly if you want to keep the current ImGui font if the font is not ready.
- /// Alternatively, use to wait for this property to become true.
+ /// Alternatively, use to wait for this property to become true.
///
bool Available { get; }
- ///
- /// Attempts to lock the fully constructed instance of corresponding to the this
- /// , for use in any thread.
- /// Modification of the font will exhibit undefined behavior if some other thread also uses the font.
- ///
- /// The error message, if any.
- ///
- /// An instance of that must be disposed after use on success;
- /// null with populated on failure.
- ///
- ILockedImFont? TryLock(out string? errorMessage);
-
///
/// Locks the fully constructed instance of corresponding to the this
/// , for use in any thread.
@@ -105,11 +92,4 @@ public interface IFontHandle : IDisposable
///
/// A task containing this .
Task WaitAsync();
-
- ///
- /// Waits for to become true.
- ///
- /// The cancellation token.
- /// A task containing this .
- Task WaitAsync(CancellationToken cancellationToken);
}
diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs
index 98a823deb..1fdaf4596 100644
--- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs
+++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs
@@ -238,17 +238,12 @@ internal abstract class FontHandle : IFontHandle
}
///
- public Task WaitAsync() => this.WaitAsync(CancellationToken.None);
-
- ///
- public Task WaitAsync(CancellationToken cancellationToken)
+ public Task WaitAsync()
{
if (this.Available)
return Task.FromResult(this);
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- cancellationToken.Register(() => tcs.TrySetCanceled());
-
this.ImFontChanged += OnImFontChanged;
this.Disposed += OnDisposed;
if (this.Available)
diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.FromSeString.cs b/Dalamud/Interface/Textures/Internal/TextureManager.FromSeString.cs
deleted file mode 100644
index 3e90ae3ea..000000000
--- a/Dalamud/Interface/Textures/Internal/TextureManager.FromSeString.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Dalamud.Interface.ImGuiSeStringRenderer;
-using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
-using Dalamud.Interface.Textures.TextureWraps;
-using Dalamud.Utility;
-
-namespace Dalamud.Interface.Textures.Internal;
-
-/// Service responsible for loading and disposing ImGui texture wraps.
-internal sealed partial class TextureManager
-{
- [ServiceManager.ServiceDependency]
- private readonly SeStringRenderer seStringRenderer = Service.Get();
-
- ///
- public IDalamudTextureWrap CreateTextureFromSeString(
- ReadOnlySpan text,
- scoped in SeStringDrawParams drawParams = default,
- string? debugName = null)
- {
- ThreadSafety.AssertMainThread();
- using var dd = this.seStringRenderer.CreateDrawData(text, drawParams);
- var texture = this.CreateDrawListTexture(debugName ?? nameof(this.CreateTextureFromSeString));
- try
- {
- texture.Size = dd.Data.DisplaySize;
- texture.Draw(dd.DataPtr);
- return texture;
- }
- catch
- {
- texture.Dispose();
- throw;
- }
- }
-}
diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.cs b/Dalamud/Interface/Textures/Internal/TextureManager.cs
index d0f0d8c07..059c716ce 100644
--- a/Dalamud/Interface/Textures/Internal/TextureManager.cs
+++ b/Dalamud/Interface/Textures/Internal/TextureManager.cs
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Data;
using Dalamud.Game;
-using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Textures.Internal.SharedImmediateTextures;
using Dalamud.Interface.Textures.TextureWraps;
@@ -249,7 +248,7 @@ internal sealed partial class TextureManager
usage = D3D11_USAGE.D3D11_USAGE_DYNAMIC;
else
usage = D3D11_USAGE.D3D11_USAGE_DEFAULT;
-
+
using var texture = this.device.CreateTexture2D(
new()
{
diff --git a/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs b/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs
index ac6de7dd7..9b0fa0943 100644
--- a/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs
+++ b/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs
@@ -6,7 +6,6 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
-using Dalamud.Interface.ImGuiSeStringRenderer;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.IoC;
@@ -284,18 +283,6 @@ internal sealed class TextureManagerPluginScoped
return textureWrap;
}
- ///
- public IDalamudTextureWrap CreateTextureFromSeString(
- ReadOnlySpan text,
- scoped in SeStringDrawParams drawParams = default,
- string? debugName = null)
- {
- var manager = this.ManagerOrThrow;
- var textureWrap = manager.CreateTextureFromSeString(text, drawParams, debugName);
- manager.Blame(textureWrap, this.plugin);
- return textureWrap;
- }
-
///
public IEnumerable GetSupportedImageDecoderInfos() =>
this.ManagerOrThrow.Wic.GetSupportedDecoderInfos();
diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs
index 1ea0d9f2f..e38537018 100644
--- a/Dalamud/Interface/UiBuilder.cs
+++ b/Dalamud/Interface/UiBuilder.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics;
-using System.Threading;
using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
@@ -692,14 +691,13 @@ public sealed class UiBuilder : IDisposable, IUiBuilder
FontAtlasAutoRebuildMode autoRebuildMode,
bool isGlobalScaled = true,
string? debugName = null) =>
- this.scopedFinalizer.Add(
- Service
- .Get()
- .CreateFontAtlas(
- this.namespaceName + ":" + (debugName ?? "custom"),
- autoRebuildMode,
- isGlobalScaled,
- this.plugin));
+ this.scopedFinalizer.Add(Service
+ .Get()
+ .CreateFontAtlas(
+ this.namespaceName + ":" + (debugName ?? "custom"),
+ autoRebuildMode,
+ isGlobalScaled,
+ this.plugin));
///
/// Unregister the UiBuilder. Do not call this in plugin code.
@@ -870,15 +868,6 @@ public sealed class UiBuilder : IDisposable, IUiBuilder
// Note: do not dispose w; we do not own it
}
- public ILockedImFont? TryLock(out string? errorMessage)
- {
- if (this.wrapped is { } w)
- return w.TryLock(out errorMessage);
-
- errorMessage = nameof(ObjectDisposedException);
- return null;
- }
-
public ILockedImFont Lock() =>
this.wrapped?.Lock() ?? throw new ObjectDisposedException(nameof(FontHandleWrapper));
@@ -887,13 +876,7 @@ public sealed class UiBuilder : IDisposable, IUiBuilder
public void Pop() => this.WrappedNotDisposed.Pop();
public Task WaitAsync() =>
- this.wrapped?.WaitAsync().ContinueWith(_ => (IFontHandle)this)
- ?? Task.FromException(new ObjectDisposedException(nameof(FontHandleWrapper)));
-
- public Task WaitAsync(CancellationToken cancellationToken) =>
- this.wrapped?.WaitAsync(cancellationToken)
- .ContinueWith(_ => (IFontHandle)this, cancellationToken)
- ?? Task.FromException(new ObjectDisposedException(nameof(FontHandleWrapper)));
+ this.WrappedNotDisposed.WaitAsync().ContinueWith(_ => (IFontHandle)this);
public override string ToString() =>
$"{nameof(FontHandleWrapper)}({this.wrapped?.ToString() ?? "disposed"})";
diff --git a/Dalamud/Interface/Utility/BufferBackedImDrawData.cs b/Dalamud/Interface/Utility/BufferBackedImDrawData.cs
deleted file mode 100644
index e6128992a..000000000
--- a/Dalamud/Interface/Utility/BufferBackedImDrawData.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-using Dalamud.Bindings.ImGui;
-
-namespace Dalamud.Interface.Utility;
-
-/// Wrapper aroundx containing one .
-public unsafe struct BufferBackedImDrawData : IDisposable
-{
- private nint buffer;
-
- /// Initializes a new instance of the struct.
- /// Address of buffer to use.
- private BufferBackedImDrawData(nint buffer) => this.buffer = buffer;
-
- /// Gets the stored in this buffer.
- public readonly ref ImDrawData Data => ref ((DataStruct*)this.buffer)->Data;
-
- /// Gets the stored in this buffer.
- public readonly ImDrawDataPtr DataPtr => new((ImDrawData*)Unsafe.AsPointer(ref this.Data));
-
- /// Gets the stored in this buffer.
- public readonly ref ImDrawList List => ref ((DataStruct*)this.buffer)->List;
-
- /// Gets the stored in this buffer.
- public readonly ImDrawListPtr ListPtr => new((ImDrawList*)Unsafe.AsPointer(ref this.List));
-
- /// Creates a new instance of .
- /// A new instance of .
- public static BufferBackedImDrawData Create()
- {
- if (ImGui.GetCurrentContext().IsNull || ImGui.GetIO().FontDefault.Handle is null)
- throw new("ImGui is not ready");
-
- var res = new BufferBackedImDrawData(Marshal.AllocHGlobal(sizeof(DataStruct)));
- var ds = (DataStruct*)res.buffer;
- *ds = default;
-
- var atlas = ImGui.GetIO().Fonts;
- ds->SharedData = *ImGui.GetDrawListSharedData().Handle;
- ds->SharedData.TexIdCommon = atlas.Textures[atlas.TextureIndexCommon].TexID;
- ds->SharedData.TexUvWhitePixel = atlas.TexUvWhitePixel;
- ds->SharedData.TexUvLines = (Vector4*)Unsafe.AsPointer(ref atlas.TexUvLines[0]);
- ds->SharedData.Font = ImGui.GetIO().FontDefault;
- ds->SharedData.FontSize = ds->SharedData.Font->FontSize;
- ds->SharedData.ClipRectFullscreen = new(
- float.NegativeInfinity,
- float.NegativeInfinity,
- float.PositiveInfinity,
- float.PositiveInfinity);
-
- ds->List.Data = &ds->SharedData;
- ds->ListPtr = &ds->List;
- ds->Data.CmdLists = &ds->ListPtr;
- ds->Data.CmdListsCount = 1;
- ds->Data.FramebufferScale = Vector2.One;
-
- res.ListPtr._ResetForNewFrame();
- res.ListPtr.PushClipRectFullScreen();
- res.ListPtr.PushTextureID(new(atlas.TextureIndexCommon));
- return res;
- }
-
- /// Updates the statistics information stored in from .
- public readonly void UpdateDrawDataStatistics()
- {
- this.Data.TotalIdxCount = this.List.IdxBuffer.Size;
- this.Data.TotalVtxCount = this.List.VtxBuffer.Size;
- }
-
- ///
- public void Dispose()
- {
- if (this.buffer != 0)
- {
- this.ListPtr._ClearFreeMemory();
- Marshal.FreeHGlobal(this.buffer);
- this.buffer = 0;
- }
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct DataStruct
- {
- public ImDrawData Data;
- public ImDrawList* ListPtr;
- public ImDrawList List;
- public ImDrawListSharedData SharedData;
- }
-}
diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs
index ee2840d3d..87f258250 100644
--- a/Dalamud/Interface/Utility/ImGuiHelpers.cs
+++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs
@@ -575,15 +575,6 @@ public static partial class ImGuiHelpers
public static unsafe ImFontPtr OrElse(this ImFontPtr self, ImFontPtr other) =>
self.IsNull ? other : self;
- /// Creates a draw data that will draw the given SeString onto it.
- /// SeString to render.
- /// Initial rendering style.
- /// A new self-contained draw data.
- internal static BufferBackedImDrawData CreateDrawData(
- ReadOnlySpan sss,
- scoped in SeStringDrawParams style = default) =>
- Service.Get().CreateDrawData(sss, style);
-
///
/// Mark 4K page as used, after adding a codepoint to a font.
///
diff --git a/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs b/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs
index 4a0137c88..86435e8c1 100644
--- a/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs
+++ b/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs
@@ -6,12 +6,10 @@ using System.Text;
using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
-using Dalamud.Game;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal;
-using Dalamud.Interface.Internal.Windows.Data.Widgets;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Textures.TextureWraps;
using Serilog;
@@ -35,14 +33,6 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService
this.interfaceManager.Draw += this.InterfaceManagerOnDraw;
}
- private enum ContextMenuActionType
- {
- None,
- SaveAsFile,
- CopyToClipboard,
- SendToTexWidget,
- }
-
///
void IInternalDisposableService.DisposeService() => this.interfaceManager.Draw -= this.InterfaceManagerOnDraw;
@@ -76,16 +66,15 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService
var textureManager = await Service.GetAsync();
var popupName = $"{nameof(this.ShowTextureSaveMenuAsync)}_{textureWrap.Handle.Handle:X}";
- ContextMenuActionType action;
BitmapCodecInfo? encoder;
{
var first = true;
var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList();
- var tcs = new TaskCompletionSource<(ContextMenuActionType Action, BitmapCodecInfo? Codec)>(
+ var tcs = new TaskCompletionSource(
TaskCreationOptions.RunContinuationsAsynchronously);
Service.Get().Draw += DrawChoices;
- (action, encoder) = await tcs.Task;
+ encoder = await tcs.Task;
[SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "This shall not escape")]
void DrawChoices()
@@ -109,20 +98,13 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService
}
if (ImGui.Selectable("Copy"u8))
- tcs.TrySetResult((ContextMenuActionType.CopyToClipboard, null));
- if (ImGui.Selectable("Send to TexWidget"u8))
- tcs.TrySetResult((ContextMenuActionType.SendToTexWidget, null));
-
- ImGui.Separator();
-
+ tcs.TrySetResult(null);
foreach (var encoder2 in encoders)
{
if (ImGui.Selectable(encoder2.Name))
- tcs.TrySetResult((ContextMenuActionType.SaveAsFile, encoder2));
+ tcs.TrySetResult(encoder2);
}
- ImGui.Separator();
-
const float previewImageWidth = 320;
var size = textureWrap.Size;
if (size.X > previewImageWidth)
@@ -138,68 +120,50 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService
}
}
- switch (action)
+ if (encoder is null)
{
- case ContextMenuActionType.CopyToClipboard:
- isCopy = true;
- await textureManager.CopyToClipboardAsync(textureWrap, name, true);
- break;
+ isCopy = true;
+ await textureManager.CopyToClipboardAsync(textureWrap, name, true);
+ }
+ else
+ {
+ var props = new Dictionary();
+ if (encoder.ContainerGuid == GUID.GUID_ContainerFormatTiff)
+ props["CompressionQuality"] = 1.0f;
+ else if (encoder.ContainerGuid == GUID.GUID_ContainerFormatJpeg ||
+ encoder.ContainerGuid == GUID.GUID_ContainerFormatHeif ||
+ encoder.ContainerGuid == GUID.GUID_ContainerFormatWmp)
+ props["ImageQuality"] = 1.0f;
- case ContextMenuActionType.SendToTexWidget:
- {
- var framework = await Service.GetAsync();
- var dalamudInterface = await Service.GetAsync();
- await framework.RunOnFrameworkThread(
- () =>
- {
- var texWidget = dalamudInterface.GetDataWindowWidget();
- dalamudInterface.SetDataWindowWidget(texWidget);
- texWidget.AddTexture(Task.FromResult(textureWrap.CreateWrapSharingLowLevelResource()));
- });
- break;
- }
-
- case ContextMenuActionType.SaveAsFile when encoder is not null:
- {
- var props = new Dictionary();
- if (encoder.ContainerGuid == GUID.GUID_ContainerFormatTiff)
- props["CompressionQuality"] = 1.0f;
- else if (encoder.ContainerGuid == GUID.GUID_ContainerFormatJpeg ||
- encoder.ContainerGuid == GUID.GUID_ContainerFormatHeif ||
- encoder.ContainerGuid == GUID.GUID_ContainerFormatWmp)
- props["ImageQuality"] = 1.0f;
-
- var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- this.fileDialogManager.SaveFileDialog(
- "Save texture...",
- $"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}",
- name + encoder.Extensions.First(),
- encoder.Extensions.First(),
- (ok, path2) =>
- {
- if (!ok)
- tcs.SetCanceled();
- else
- tcs.SetResult(path2);
- });
- var path = await tcs.Task.ConfigureAwait(false);
-
- await textureManager.SaveToFileAsync(textureWrap, encoder.ContainerGuid, path, props: props);
-
- var notif = Service.Get().AddNotification(
- new()
- {
- Content = $"File saved to: {path}",
- Title = initiatorName,
- Type = NotificationType.Success,
- });
- notif.Click += n =>
+ var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ this.fileDialogManager.SaveFileDialog(
+ "Save texture...",
+ $"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}",
+ name + encoder.Extensions.First(),
+ encoder.Extensions.First(),
+ (ok, path2) =>
{
- Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
- n.Notification.DismissNow();
- };
- break;
- }
+ if (!ok)
+ tcs.SetCanceled();
+ else
+ tcs.SetResult(path2);
+ });
+ var path = await tcs.Task.ConfigureAwait(false);
+
+ await textureManager.SaveToFileAsync(textureWrap, encoder.ContainerGuid, path, props: props);
+
+ var notif = Service.Get().AddNotification(
+ new()
+ {
+ Content = $"File saved to: {path}",
+ Title = initiatorName,
+ Type = NotificationType.Success,
+ });
+ notif.Click += n =>
+ {
+ Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
+ n.Notification.DismissNow();
+ };
}
}
catch (Exception e)
diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs
index b0786fbb5..e90e38119 100644
--- a/Dalamud/Interface/Windowing/Window.cs
+++ b/Dalamud/Interface/Windowing/Window.cs
@@ -1,6 +1,9 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using System.Numerics;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using CheapLoc;
@@ -16,13 +19,10 @@ using Dalamud.Interface.Utility.Internal;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing.Persistence;
using Dalamud.Logging.Internal;
+using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.UI;
-using TerraFX.Interop.Windows;
-
-using static TerraFX.Interop.Windows.Windows;
-
namespace Dalamud.Interface.Windowing;
///
@@ -31,15 +31,11 @@ namespace Dalamud.Interface.Windowing;
public abstract class Window
{
private const float FadeInOutTime = 0.072f;
- private const string AdditionsPopupName = "WindowSystemContextActions";
private static readonly ModuleLog Log = new("WindowSystem");
private static bool wasEscPressedLastFrame = false;
- private readonly TitleBarButton additionsButton;
- private readonly List allButtons = [];
-
private bool internalLastIsOpen = false;
private bool internalIsOpen = false;
private bool internalIsPinned = false;
@@ -76,20 +72,6 @@ public abstract class Window
this.WindowName = name;
this.Flags = flags;
this.ForceMainWindow = forceMainWindow;
-
- this.additionsButton = new()
- {
- Icon = FontAwesomeIcon.Bars,
- IconOffset = new Vector2(2.5f, 1),
- Click = _ =>
- {
- this.internalIsClickthrough = false;
- this.presetDirty = false;
- ImGui.OpenPopup(AdditionsPopupName);
- },
- Priority = int.MinValue,
- AvailableClickthrough = true,
- };
}
///
@@ -500,12 +482,14 @@ public abstract class Window
ImGuiP.GetCurrentWindow().InheritNoInputs = this.internalIsClickthrough;
}
- if (ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
+ // Not supported yet on non-main viewports
+ if ((this.internalIsPinned || this.internalIsClickthrough || this.internalAlpha.HasValue) &&
+ ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
{
- if ((flags & ImGuiWindowFlags.NoInputs) == ImGuiWindowFlags.NoInputs)
- ImGui.GetWindowViewport().Flags |= ImGuiViewportFlags.NoInputs;
- else
- ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs;
+ this.internalAlpha = null;
+ this.internalIsPinned = false;
+ this.internalIsClickthrough = false;
+ this.presetDirty = true;
}
if (this.hasError)
@@ -529,6 +513,7 @@ public abstract class Window
}
}
+ const string additionsPopupName = "WindowSystemContextActions";
var flagsApplicableForTitleBarIcons = !flags.HasFlag(ImGuiWindowFlags.NoDecoration) &&
!flags.HasFlag(ImGuiWindowFlags.NoTitleBar);
var showAdditions = (this.AllowPinning || this.AllowClickthrough) &&
@@ -539,8 +524,13 @@ public abstract class Window
{
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
- if (ImGui.BeginPopup(AdditionsPopupName, ImGuiWindowFlags.NoMove))
+ if (ImGui.BeginPopup(additionsPopupName, ImGuiWindowFlags.NoMove))
{
+ var isAvailable = ImGuiHelpers.CheckIsWindowOnMainViewport();
+
+ if (!isAvailable)
+ ImGui.BeginDisabled();
+
if (this.internalIsClickthrough)
ImGui.BeginDisabled();
@@ -588,11 +578,21 @@ public abstract class Window
this.presetDirty = true;
}
- ImGui.TextColored(
- ImGuiColors.DalamudGrey,
- Loc.Localize(
- "WindowSystemContextActionClickthroughDisclaimer",
- "Open this menu again by clicking the three dashes to disable clickthrough."));
+ if (isAvailable)
+ {
+ ImGui.TextColored(ImGuiColors.DalamudGrey,
+ Loc.Localize("WindowSystemContextActionClickthroughDisclaimer",
+ "Open this menu again by clicking the three dashes to disable clickthrough."));
+ }
+ else
+ {
+ ImGui.TextColored(ImGuiColors.DalamudGrey,
+ Loc.Localize("WindowSystemContextActionViewportDisclaimer",
+ "These features are only available if this window is inside the game window."));
+ }
+
+ if (!isAvailable)
+ ImGui.EndDisabled();
if (ImGui.Button(Loc.Localize("WindowSystemContextActionPrintWindow", "Print window")))
printWindow = true;
@@ -603,15 +603,34 @@ public abstract class Window
ImGui.PopStyleVar();
}
- if (flagsApplicableForTitleBarIcons)
+ unsafe
{
- this.allButtons.Clear();
- this.allButtons.EnsureCapacity(this.TitleBarButtons.Count + 1);
- this.allButtons.AddRange(this.TitleBarButtons);
- if (showAdditions)
- this.allButtons.Add(this.additionsButton);
- this.allButtons.Sort(static (a, b) => b.Priority - a.Priority);
- this.DrawTitleBarButtons();
+ var window = ImGuiP.GetCurrentWindow();
+
+ ImRect outRect;
+ ImGuiP.TitleBarRect(&outRect, window);
+
+ var additionsButton = new TitleBarButton
+ {
+ Icon = FontAwesomeIcon.Bars,
+ IconOffset = new Vector2(2.5f, 1),
+ Click = _ =>
+ {
+ this.internalIsClickthrough = false;
+ this.presetDirty = false;
+ ImGui.OpenPopup(additionsPopupName);
+ },
+ Priority = int.MinValue,
+ AvailableClickthrough = true,
+ };
+
+ if (flagsApplicableForTitleBarIcons)
+ {
+ this.DrawTitleBarButtons(window, flags, outRect,
+ showAdditions
+ ? this.TitleBarButtons.Append(additionsButton)
+ : this.TitleBarButtons);
+ }
}
if (wasFocused)
@@ -778,11 +797,8 @@ public abstract class Window
}
}
- private unsafe void DrawTitleBarButtons()
+ private unsafe void DrawTitleBarButtons(ImGuiWindowPtr window, ImGuiWindowFlags flags, ImRect titleBarRect, IEnumerable buttons)
{
- var window = ImGuiP.GetCurrentWindow();
- var flags = window.Flags;
- var titleBarRect = window.TitleBarRect();
ImGui.PushClipRect(ImGui.GetWindowPos(), ImGui.GetWindowPos() + ImGui.GetWindowSize(), false);
var style = ImGui.GetStyle();
@@ -817,22 +833,26 @@ public abstract class Window
var max = pos + new Vector2(fontSize, fontSize);
ImRect bb = new(pos, max);
var isClipped = !ImGuiP.ItemAdd(bb, id, null, 0);
- bool hovered, held, pressed;
+ bool hovered, held;
+ var pressed = false;
if (this.internalIsClickthrough)
{
+ hovered = false;
+ held = false;
+
// ButtonBehavior does not function if the window is clickthrough, so we have to do it ourselves
- var pad = ImGui.GetStyle().TouchExtraPadding;
- var rect = new ImRect(pos - pad, max + pad);
- hovered = rect.Contains(ImGui.GetMousePos());
+ if (ImGui.IsMouseHoveringRect(pos, max))
+ {
+ hovered = true;
- // Temporarily enable inputs
- // This will be reset on next frame, and then enabled again if it is still being hovered
- if (hovered && ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
- ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs;
-
- // We can't use ImGui native functions here, because they don't work with clickthrough
- pressed = held = hovered && (GetKeyState(VK.VK_LBUTTON) & 0x8000) != 0;
+ // We can't use ImGui native functions here, because they don't work with clickthrough
+ if ((Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0)
+ {
+ held = true;
+ pressed = true;
+ }
+ }
}
else
{
@@ -861,7 +881,7 @@ public abstract class Window
return pressed;
}
- foreach (var button in this.allButtons)
+ foreach (var button in buttons.OrderBy(x => x.Priority))
{
if (this.internalIsClickthrough && !button.AvailableClickthrough)
return;
@@ -1015,7 +1035,7 @@ public abstract class Window
///
/// Gets or sets an action that is called when the button is clicked.
///
- public Action? Click { get; set; }
+ public Action Click { get; set; }
///
/// Gets or sets the priority the button shall be shown in.
diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs
index dc0522aa8..7cd1b7c86 100644
--- a/Dalamud/Plugin/Services/ITextureProvider.cs
+++ b/Dalamud/Plugin/Services/ITextureProvider.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
@@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
-using Dalamud.Interface.ImGuiSeStringRenderer;
using Dalamud.Interface.Internal.Windows.Data.Widgets;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Textures.TextureWraps;
@@ -187,17 +186,6 @@ public interface ITextureProvider : IDalamudService
string? debugName = null,
CancellationToken cancellationToken = default);
- /// Creates a texture by drawing a SeString onto it.
- /// SeString to render.
- /// Parameters for drawing.
- /// Name for debug display purposes.
- /// The new texture.
- /// Can be only be used from the main thread.
- public IDalamudTextureWrap CreateTextureFromSeString(
- ReadOnlySpan text,
- scoped in SeStringDrawParams drawParams = default,
- string? debugName = null);
-
/// Gets the supported bitmap decoders.
/// The supported bitmap decoders.
///
diff --git a/imgui/Dalamud.Bindings.ImGui/ImVector.cs b/imgui/Dalamud.Bindings.ImGui/ImVector.cs
index 67e450193..9a10c1d6b 100644
--- a/imgui/Dalamud.Bindings.ImGui/ImVector.cs
+++ b/imgui/Dalamud.Bindings.ImGui/ImVector.cs
@@ -1,12 +1,7 @@
-using System.Collections;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
namespace Dalamud.Bindings.ImGui;
-///
-/// A structure representing a dynamic array for unmanaged types.
-///
public unsafe struct ImVector
{
public readonly int Size;
@@ -20,23 +15,23 @@ public unsafe struct ImVector
Data = data;
}
- public readonly ref T Ref(int index) => ref Unsafe.AsRef((byte*)this.Data + (index * Unsafe.SizeOf()));
+ public ref T Ref(int index)
+ {
+ return ref Unsafe.AsRef((byte*)Data + index * Unsafe.SizeOf());
+ }
- public readonly nint Address(int index) => (nint)((byte*)this.Data + (index * Unsafe.SizeOf()));
+ public IntPtr Address(int index)
+ {
+ return (IntPtr)((byte*)Data + index * Unsafe.SizeOf());
+ }
}
///
/// A structure representing a dynamic array for unmanaged types.
///
/// The type of elements in the vector, must be unmanaged.
-[StructLayout(LayoutKind.Sequential)]
-public unsafe struct ImVector : IEnumerable
- where T : unmanaged
+public unsafe struct ImVector where T : unmanaged
{
- private int size;
- private int capacity;
- private T* data;
-
///
/// Initializes a new instance of the struct with the specified size, capacity, and data pointer.
///
@@ -50,6 +45,11 @@ public unsafe struct ImVector : IEnumerable
this.data = data;
}
+ private int size;
+ private int capacity;
+ private unsafe T* data;
+
+
///
/// Gets or sets the element at the specified index.
///
@@ -58,72 +58,80 @@ public unsafe struct ImVector : IEnumerable
/// Thrown when the index is out of range.
public T this[int index]
{
- readonly get
+ get
{
- if (index < 0 || index >= this.size)
+ if (index < 0 || index >= size)
+ {
throw new IndexOutOfRangeException();
- return this.data[index];
+ }
+ return data[index];
}
set
{
- if (index < 0 || index >= this.size)
+ if (index < 0 || index >= size)
+ {
throw new IndexOutOfRangeException();
- this.data[index] = value;
+ }
+ data[index] = value;
}
}
///
/// Gets a pointer to the first element of the vector.
///
- public readonly T* Data => this.data;
+ public readonly T* Data => data;
///
/// Gets a pointer to the first element of the vector.
///
- public readonly T* Front => this.data;
+ public readonly T* Front => data;
///
/// Gets a pointer to the last element of the vector.
///
- public readonly T* Back => this.size > 0 ? this.data + this.size - 1 : null;
+ public readonly T* Back => size > 0 ? data + size - 1 : null;
///
/// Gets or sets the capacity of the vector.
///
public int Capacity
{
- readonly get => this.capacity;
+ readonly get => capacity;
set
{
- ArgumentOutOfRangeException.ThrowIfLessThan(value, this.size, nameof(Capacity));
- if (this.capacity == value)
- return;
-
- if (this.data == null)
+ if (capacity == value)
{
- this.data = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T)));
+ return;
+ }
+
+ if (data == null)
+ {
+ data = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T)));
}
else
{
- var newSize = Math.Min(this.size, value);
- var newData = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T)));
- Buffer.MemoryCopy(this.data, newData, (nuint)(value * sizeof(T)), (nuint)(newSize * sizeof(T)));
- ImGui.MemFree(this.data);
- this.data = newData;
- this.size = newSize;
+ int newSize = Math.Min(size, value);
+ T* newData = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T)));
+ Buffer.MemoryCopy(data, newData, (nuint)(value * sizeof(T)), (nuint)(newSize * sizeof(T)));
+ ImGui.MemFree(data);
+ data = newData;
+ size = newSize;
}
- this.capacity = value;
+ capacity = value;
// Clear the rest of the data
- new Span(this.data + this.size, this.capacity - this.size).Clear();
+ for (int i = size; i < capacity; i++)
+ {
+ data[i] = default;
+ }
}
}
///
/// Gets the number of elements in the vector.
///
- public readonly int Size => this.size;
+ public readonly int Size => size;
///
/// Grows the capacity of the vector to at least the specified value.
@@ -131,8 +139,10 @@ public unsafe struct ImVector : IEnumerable
/// The new capacity.
public void Grow(int newCapacity)
{
- var newCapacity2 = this.capacity > 0 ? this.capacity + (this.capacity / 2) : 8;
- this.Capacity = newCapacity2 > newCapacity ? newCapacity2 : newCapacity;
+ if (newCapacity > capacity)
+ {
+ Capacity = newCapacity * 2;
+ }
}
///
@@ -141,8 +151,10 @@ public unsafe struct ImVector : IEnumerable
/// The minimum capacity required.
public void EnsureCapacity(int size)
{
- if (size > this.capacity)
+ if (size > capacity)
+ {
Grow(size);
+ }
}
///
@@ -152,46 +164,25 @@ public unsafe struct ImVector : IEnumerable
public void Resize(int newSize)
{
EnsureCapacity(newSize);
- this.size = newSize;
+ size = newSize;
}
///
/// Clears all elements from the vector.
///
- public void Clear() => this.size = 0;
+ public void Clear()
+ {
+ size = 0;
+ }
///
/// Adds an element to the end of the vector.
///
/// The value to add.
- [OverloadResolutionPriority(1)]
public void PushBack(T value)
{
- this.EnsureCapacity(this.size + 1);
- this.data[this.size++] = value;
- }
-
- ///
- /// Adds an element to the end of the vector.
- ///
- /// The value to add.
- [OverloadResolutionPriority(2)]
- public void PushBack(in T value)
- {
- EnsureCapacity(this.size + 1);
- this.data[this.size++] = value;
- }
-
- ///
- /// Adds an element to the front of the vector.
- ///
- /// The value to add.
- public void PushFront(in T value)
- {
- if (this.size == 0)
- this.PushBack(value);
- else
- this.Insert(0, value);
+ EnsureCapacity(size + 1);
+ data[size++] = value;
}
///
@@ -199,126 +190,48 @@ public unsafe struct ImVector : IEnumerable
///
public void PopBack()
{
- if (this.size > 0)
+ if (size > 0)
{
- this.size--;
+ size--;
}
}
- public ref T Insert(int index, in T v) {
- ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(index));
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, this.size, nameof(index));
- this.EnsureCapacity(this.size + 1);
- if (index < this.size)
- {
- Buffer.MemoryCopy(
- this.data + index,
- this.data + index + 1,
- (this.size - index) * sizeof(T),
- (this.size - index) * sizeof(T));
- }
-
- this.data[index] = v;
- this.size++;
- return ref this.data[index];
- }
-
- public Span InsertRange(int index, ReadOnlySpan v)
- {
- ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(index));
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, this.size, nameof(index));
- this.EnsureCapacity(this.size + v.Length);
- if (index < this.size)
- {
- Buffer.MemoryCopy(
- this.data + index,
- this.data + index + v.Length,
- (this.size - index) * sizeof(T),
- (this.size - index) * sizeof(T));
- }
-
- var dstSpan = new Span(this.data + index, v.Length);
- v.CopyTo(new(this.data + index, v.Length));
- this.size += v.Length;
- return dstSpan;
- }
-
///
/// Frees the memory allocated for the vector.
///
public void Free()
{
- if (this.data != null)
+ if (data != null)
{
- ImGui.MemFree(this.data);
- this.data = null;
- this.size = 0;
- this.capacity = 0;
+ ImGui.MemFree(data);
+ data = null;
+ size = 0;
+ capacity = 0;
}
}
- public readonly ref T Ref(int index)
+ public ref T Ref(int index)
{
- return ref Unsafe.AsRef((byte*)Data + (index * Unsafe.SizeOf()));
+ return ref Unsafe.AsRef((byte*)Data + index * Unsafe.SizeOf());
}
- public readonly ref TCast Ref(int index)
+ public ref TCast Ref(int index)
{
- return ref Unsafe.AsRef((byte*)Data + (index * Unsafe.SizeOf()));
+ return ref Unsafe.AsRef((byte*)Data + index * Unsafe.SizeOf());
}
- public readonly void* Address(int index)
+ public void* Address(int index)
{
- return (byte*)Data + (index * Unsafe.SizeOf());
+ return (byte*)Data + index * Unsafe.SizeOf();
}
- public readonly void* Address(int index)
+ public void* Address(int index)
{
- return (byte*)Data + (index * Unsafe.SizeOf());
+ return (byte*)Data + index * Unsafe.SizeOf();
}
- public readonly ImVector* ToUntyped()
+ public ImVector* ToUntyped()
{
- return (ImVector*)Unsafe.AsPointer(ref Unsafe.AsRef(in this));
- }
-
- public readonly Span AsSpan() => new(this.data, this.size);
-
- public readonly Enumerator GetEnumerator() => new(this.data, this.data + this.size);
-
- readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
-
- readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
-
- public struct Enumerator(T* begin, T* end) : IEnumerator, IEnumerable
- {
- private T* current = null;
-
- public readonly ref T Current => ref *this.current;
-
- readonly T IEnumerator.Current => this.Current;
-
- readonly object IEnumerator.Current => this.Current;
-
- public bool MoveNext()
- {
- var next = this.current == null ? begin : this.current + 1;
- if (next == end)
- return false;
- this.current = next;
- return true;
- }
-
- public void Reset() => this.current = null;
-
- public readonly Enumerator GetEnumerator() => new(begin, end);
-
- readonly void IDisposable.Dispose()
- {
- }
-
- readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
-
- readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+ return (ImVector*)Unsafe.AsPointer(ref this);
}
}
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index e5dedba42..e5f586630 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit e5dedba42a3fea8f050ea54ac583a5874bf51c6f
+Subproject commit e5f586630ef06fa48d5dc0d8c0fa679323093c77
diff --git a/lib/cimgui b/lib/cimgui
index bc3272967..27c8565f6 160000
--- a/lib/cimgui
+++ b/lib/cimgui
@@ -1 +1 @@
-Subproject commit bc327296758d57d3bdc963cb6ce71dd5b0c7e54c
+Subproject commit 27c8565f631b004c3266373890e41ecc627f775b