diff --git a/HUD Manager/PluginUi.cs b/HUD Manager/PluginUi.cs index d6f56a2..d3eb12b 100644 --- a/HUD Manager/PluginUi.cs +++ b/HUD Manager/PluginUi.cs @@ -61,6 +61,9 @@ namespace HUD_Manager { private HudConditionMatch? _editingCondition; private bool _scrollToAdd; + private HashSet Previews { get; } = new(); + private HashSet UpdatePreviews { get; } = new(); + public PluginUi(Plugin plugin) { this.Plugin = plugin; } @@ -69,6 +72,175 @@ namespace HUD_Manager { this.SettingsVisible = true; } + private static Tuple CalcPosAndSize(Element element) { + // get X & Y coords from the element, which are percentages (0 - 100) + var percentagePos = new Vector2(element.X, element.Y); + + // get size in pixels + var size = new Vector2(element.Width, element.Height); + // scale size according to the element's scale + size.X = (float) Math.Round(size.X * element.Scale); + size.Y = (float) Math.Round(size.Y * element.Scale); + + // convert the percentages into pixels + var screen = ImGui.GetIO().DisplaySize; + var pixelPos = new Vector2( + (float) Math.Round(percentagePos.X * screen.X / 100), + (float) Math.Round(percentagePos.Y * screen.Y / 100) + ); + + // split the measured from into x and y parts + var (xMeasure, yMeasure) = element.MeasuredFrom.ToParts(); + + // determine subtraction values to make the coords point to the top left + var subX = xMeasure switch { + MeasuredX.Left => 0, + MeasuredX.Middle => size.X / 2, + MeasuredX.Right => size.X, + _ => throw new ArgumentOutOfRangeException(), + }; + + var subY = yMeasure switch { + MeasuredY.Top => 0, + MeasuredY.Middle => size.Y / 2, + MeasuredY.Bottom => size.Y, + _ => throw new ArgumentOutOfRangeException(), + }; + + // transform coords to top left for ImGui + pixelPos.X -= subX; + pixelPos.Y -= subY; + + // round the coords + pixelPos.X = (float) Math.Round(pixelPos.X); + pixelPos.Y = (float) Math.Round(pixelPos.Y); + + return Tuple.Create(pixelPos, size); + } + + private static Vector2 ConvertImGuiPos(Element element, Vector2 im) { + // get the coordinates in pixels + var pos = new Vector2(im.X, im.Y); + + // get the size of the element + var size = new Vector2(element.Width, element.Height); + // scale the size of the element + size.X = (float) Math.Round(size.X * element.Scale); + size.Y = (float) Math.Round(size.Y * element.Scale); + + // split the measured from into x and y parts + var (xMeasure, yMeasure) = element.MeasuredFrom.ToParts(); + + // determine how much to add to convert top left coords into the element's system + var addX = xMeasure switch { + MeasuredX.Left => 0, + MeasuredX.Middle => size.X / 2, + MeasuredX.Right => size.X, + _ => throw new ArgumentOutOfRangeException(), + }; + + var addY = yMeasure switch { + MeasuredY.Top => 0, + MeasuredY.Middle => size.Y / 2, + MeasuredY.Bottom => size.Y, + _ => throw new ArgumentOutOfRangeException(), + }; + + // convert from top left to given type + pos.X += addX; + pos.Y += addY; + + // switch (element.MeasuredFrom) { + // case MeasuredFrom.TopLeft: + // // already top left + // break; + // case MeasuredFrom.TopMiddle: + // pos.X += size.X / 2; + // break; + // case MeasuredFrom.TopRight: + // pos.X += size.X; + // break; + // case MeasuredFrom.MiddleLeft: + // pos.Y += size.Y / 2; + // break; + // case MeasuredFrom.Middle: + // pos.X += size.X / 2; + // pos.Y += size.Y / 2; + // break; + // case MeasuredFrom.MiddleRight: + // pos.X += size.X; + // pos.Y += size.Y / 2; + // break; + // case MeasuredFrom.BottomLeft: + // pos.Y += size.Y; + // break; + // case MeasuredFrom.BottomMiddle: + // pos.X += size.X / 2; + // pos.Y += size.Y; + // break; + // case MeasuredFrom.BottomRight: + // pos.X += size.X; + // pos.Y += size.Y; + // break; + // default: + // throw new ArgumentOutOfRangeException(); + // } + + // convert the pixels into percentages + var screen = ImGui.GetIO().DisplaySize; + pos.X /= screen.X / 100; + pos.Y /= screen.Y / 100; + + return pos; + } + + private void DrawPreviews(ref bool update) { + const float tolerance = 0.0001f; + const ImGuiWindowFlags flags = ImGuiWindowFlags.NoTitleBar + | ImGuiWindowFlags.NoResize + | ImGuiWindowFlags.NoFocusOnAppearing + | ImGuiWindowFlags.NoScrollbar; + + if (this._selectedEditLayout == Guid.Empty) { + return; + } + + if (!this.Plugin.Config.Layouts.TryGetValue(this._selectedEditLayout, out var layout)) { + return; + } + + foreach (var element in layout.Elements.Values) { + if (!this.Previews.Contains(element.Id)) { + continue; + } + + var (pos, size) = CalcPosAndSize(element); + if (this.UpdatePreviews.Remove(element.Id)) { + ImGui.SetNextWindowPos(pos); + } else { + ImGui.SetNextWindowPos(pos, ImGuiCond.Appearing); + } + + ImGui.SetNextWindowSize(size); + + if (!ImGui.Begin($"##uimanager-preview-{element.Id}", flags)) { + continue; + } + + ImGui.TextUnformatted(element.Id.LocalisedName(this.Plugin.Interface.Data)); + + // determine if the window has moved and update if it has + var newPos = ConvertImGuiPos(element, ImGui.GetWindowPos()); + if (Math.Abs(newPos.X - element.X) > tolerance || Math.Abs(newPos.Y - element.Y) > tolerance) { + element.X = newPos.X; + element.Y = newPos.Y; + update = true; + } + + ImGui.End(); + } + } + private void DrawSettings() { if (!this.SettingsVisible) { return; @@ -198,6 +370,8 @@ namespace HUD_Manager { var update = false; + this.DrawPreviews(ref update); + ImGui.TextUnformatted("Layout"); var nodes = Node.BuildTree(this.Plugin.Config.Layouts); @@ -397,7 +571,16 @@ namespace HUD_Manager { ImGui.TextUnformatted("Control"); - ImGui.SameLine(ImGui.GetContentRegionAvail().X - 30); + ImGui.SameLine(ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetStyle().ItemSpacing.X * 6); + if (IconButton(FontAwesomeIcon.Search, $"uimanager-preview-element-{kind}")) { + if (this.Previews.Contains(kind)) { + this.Previews.Remove(kind); + } else { + this.Previews.Add(kind); + } + } + + ImGui.SameLine(ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X * 3); if (IconButton(FontAwesomeIcon.TrashAlt, $"uimanager-remove-element-{kind}")) { toRemove.Add(kind); } @@ -455,6 +638,7 @@ namespace HUD_Manager { ImGui.EndCombo(); } + ImGui.PopItemWidth(); DrawEnabledCheckbox(element.Id, ElementComponent.X); @@ -466,7 +650,11 @@ namespace HUD_Manager { if (ImGui.DragFloat($"##x-{kind}", ref x, this._dragSpeed)) { element.X = x; update = true; + if (this.Previews.Contains(kind)) { + this.UpdatePreviews.Add(kind); + } } + ImGui.PopItemWidth(); DrawEnabledCheckbox(element.Id, ElementComponent.Y); @@ -477,7 +665,11 @@ namespace HUD_Manager { if (ImGui.DragFloat($"##y-{kind}", ref y, this._dragSpeed)) { element.Y = y; update = true; + if (this.Previews.Contains(kind)) { + this.UpdatePreviews.Add(kind); + } } + ImGui.PopItemWidth(); } else { var screen = ImGui.GetIO().DisplaySize; @@ -487,7 +679,11 @@ namespace HUD_Manager { if (ImGui.InputInt($"##x-{kind}", ref x)) { element.X = x / screen.X * 100; update = true; + if (this.Previews.Contains(kind)) { + this.UpdatePreviews.Add(kind); + } } + ImGui.PopItemWidth(); DrawEnabledCheckbox(element.Id, ElementComponent.Y); @@ -498,7 +694,11 @@ namespace HUD_Manager { if (ImGui.InputInt($"##y-{kind}", ref y)) { element.Y = y / screen.Y * 100; update = true; + if (this.Previews.Contains(kind)) { + this.UpdatePreviews.Add(kind); + } } + ImGui.PopItemWidth(); } @@ -519,6 +719,7 @@ namespace HUD_Manager { ImGui.EndCombo(); } + ImGui.PopItemWidth(); if (!kind.IsJobGauge()) { @@ -531,6 +732,7 @@ namespace HUD_Manager { element.Opacity = (byte) opacity; update = true; } + ImGui.PopItemWidth(); } @@ -547,6 +749,7 @@ namespace HUD_Manager { targetBarOpts.ShowIndependently = independent; update = true; } + ImGui.PopItemWidth(); } @@ -570,6 +773,7 @@ namespace HUD_Manager { ImGui.EndCombo(); } + ImGui.PopItemWidth(); } @@ -593,6 +797,7 @@ namespace HUD_Manager { ImGui.EndCombo(); } + ImGui.PopItemWidth(); ImGui.NextColumn(); @@ -612,6 +817,7 @@ namespace HUD_Manager { ImGui.EndCombo(); } + ImGui.PopItemWidth(); ImGui.NextColumn(); @@ -624,6 +830,7 @@ namespace HUD_Manager { statusOpts.Gamepad = focusable ? StatusGamepad.Focusable : StatusGamepad.NonFocusable; update = true; } + ImGui.PopItemWidth(); } @@ -641,6 +848,7 @@ namespace HUD_Manager { hotbarOpts.Index = (byte) Math.Max(0, Math.Min(9, hotbarIndex - 1)); update = true; } + ImGui.PopItemWidth(); } @@ -662,6 +870,7 @@ namespace HUD_Manager { ImGui.EndCombo(); } + ImGui.PopItemWidth(); } @@ -678,6 +887,7 @@ namespace HUD_Manager { gaugeOpts.Style = simple ? GaugeStyle.Simple : GaugeStyle.Normal; update = true; } + ImGui.PopItemWidth(); } diff --git a/HUD Manager/Structs/MeasuredFrom.cs b/HUD Manager/Structs/MeasuredFrom.cs index 9a9744f..d7f57ec 100755 --- a/HUD Manager/Structs/MeasuredFrom.cs +++ b/HUD Manager/Structs/MeasuredFrom.cs @@ -1,4 +1,6 @@ -namespace HUD_Manager.Structs { +using System; + +namespace HUD_Manager.Structs { public enum MeasuredFrom : byte { TopLeft = 0, TopMiddle = 1, @@ -10,4 +12,33 @@ BottomMiddle = 7, BottomRight = 8, } + + public static class MeasureFromExt { + public static Tuple ToParts(this MeasuredFrom measured) { + return measured switch { + MeasuredFrom.TopLeft => Tuple.Create(MeasuredX.Left, MeasuredY.Top), + MeasuredFrom.TopMiddle => Tuple.Create(MeasuredX.Middle, MeasuredY.Top), + MeasuredFrom.TopRight => Tuple.Create(MeasuredX.Right, MeasuredY.Top), + MeasuredFrom.MiddleLeft => Tuple.Create(MeasuredX.Left, MeasuredY.Middle), + MeasuredFrom.Middle => Tuple.Create(MeasuredX.Middle, MeasuredY.Middle), + MeasuredFrom.MiddleRight => Tuple.Create(MeasuredX.Right, MeasuredY.Middle), + MeasuredFrom.BottomLeft => Tuple.Create(MeasuredX.Left, MeasuredY.Bottom), + MeasuredFrom.BottomMiddle => Tuple.Create(MeasuredX.Middle, MeasuredY.Bottom), + MeasuredFrom.BottomRight => Tuple.Create(MeasuredX.Right, MeasuredY.Bottom), + _ => throw new ArgumentOutOfRangeException(nameof(measured), measured, null), + }; + } + } + + public enum MeasuredX { + Left, + Middle, + Right, + } + + public enum MeasuredY { + Top, + Middle, + Bottom, + } }