using System; using System.Numerics; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Shared.Input; using Robust.Shared.Maths; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Client.UserInterface.CustomControls; internal sealed class TextEditRopeViz : OSWindow { private static readonly Color[] LeafColors = CalcLeafColors(); private readonly TextEdit _textEdit; private Vector2 _panOffset; private bool _dragging; private Vector2 _dragStartOffset; private Vector2 _dragStartMouse; public TextEditRopeViz(TextEdit textEdit) { _textEdit = textEdit; MouseFilter = MouseFilterMode.Stop; } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); if (!_textEdit.IsInsideTree) UserInterfaceManager.DeferAction(Close); } protected internal override void KeyBindDown(GUIBoundKeyEventArgs args) { base.KeyBindDown(args); if (args.Function == EngineKeyFunctions.UIClick) { _dragStartOffset = _panOffset; _dragStartMouse = args.RelativePosition; _dragging = true; args.Handle(); } } protected internal override void KeyBindUp(GUIBoundKeyEventArgs args) { base.KeyBindUp(args); if (args.Function == EngineKeyFunctions.UIClick) { _dragging = false; args.Handle(); } } protected internal override void MouseMove(GUIMouseMoveEventArgs args) { base.MouseMove(args); if (!_dragging) return; _panOffset = args.RelativePosition - _dragStartMouse + _dragStartOffset; } protected override void Shown() { base.Shown(); Root!.Name = nameof(TextEditRopeViz); } protected internal override void Draw(DrawingHandleScreen handle) { var root = _textEdit.TextRope; var totalDepth = root.Depth; DrawNode(root, _panOffset, 0, out _); float DrawNode(Rope.Node node, Vector2 offset, int depth, out Vector2 nodePos) { switch (node) { case Rope.Branch branch: { var depthOffset = 20 + (totalDepth - depth) * 4; var leftWidth = DrawNode(branch.Left, offset + new Vector2(0, depthOffset), depth + 1, out var leftPos); var rightWidth = 0f; Vector2? rightPos = null; if (branch.Right is { } right) { rightWidth = DrawNode(right, offset + new Vector2(leftWidth, depthOffset), depth + 1, out var rightPosOut); rightPos = rightPosOut; } nodePos = offset + new Vector2(leftWidth, 0); handle.DrawLine(nodePos, leftPos, Color.DarkGray); if (rightPos is { } rp) { handle.DrawLine(nodePos, rp, Color.DarkGray); } else { handle.DrawLine(nodePos, nodePos + new Vector2(10, 10), Color.Red); } handle.DrawRect(new UIBox2(nodePos - new Vector2(1, 1), nodePos + new Vector2(2, 2)), Color.White); return leftWidth + rightWidth; } case Rope.Leaf leaf: { var colorIdx = leaf.Text.Length; var color = colorIdx < LeafColors.Length ? LeafColors[colorIdx] : LeafColors[^1]; handle.DrawRect(new UIBox2(offset - new Vector2(2, 2), offset + new Vector2(3, 3)), color); nodePos = offset; return 6; } default: throw new ArgumentOutOfRangeException(nameof(node)); } } } private static Color[] CalcLeafColors() { var colors = new Color[21]; colors[0] = Color.Purple; InterpColors(colors.AsSpan(1, 10), Color.Red, Color.Lime); InterpColors(colors.AsSpan(10, 10), Color.Red, Color.Lime); static void InterpColors(Span colors, Color α, Color β) { α = Color.FromSrgb(α); β = Color.FromSrgb(β); for (var i = 0; i < colors.Length; i++) { var λ = (float)i / (colors.Length - 1); var color = Color.InterpolateBetween(α, β, λ); colors[i] = Color.ToSrgb(color); } } return colors; } }