using System; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Net.Http; using System.Numerics; using System.Text; using Dashboard.Drawing; namespace Dashboard.ImmediateUI { public class DimUIConfig { public Vector2 Margin = new Vector2(8, 4); public Vector2 Padding = new Vector2(4); public required IFont Font { get; init; } public IBrush TextBrush = new SolidBrush(Color.Black); public IBrush DisabledText = new SolidBrush(Color.Gray); public float ButtonBorderSize = 2f; public IBrush ButtonBorderBrush = new SolidBrush(Color.SteelBlue); public IBrush ButtonFillBrush = new SolidBrush(Color.SlateGray); public IBrush? ButtonShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue)); public float ButtonShadowOffset = 2f; public float InputBorderSize = 2f; public IBrush InputPlaceholderTextBrush = new SolidBrush(Color.SteelBlue); public IBrush InputBorderBrush = new SolidBrush(Color.SlateGray); public IBrush InputFillBrush = new SolidBrush(Color.LightGray); public IBrush? InputShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue)); public float InputShadowOffset = -2f; public float MenuBorderSize = 2f; public IBrush MenuBorderBrush = new SolidBrush(Color.SteelBlue); public IBrush MenuFillBrush = new SolidBrush(Color.SlateGray); public IBrush? MenuShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue)); public float MenuShadowOffset = 2f; } public class DimUI { private readonly DimUIConfig _config; private Vector2 _pen; private Box2d _bounds; private bool _firstLine = false; private bool _sameLine = false; private float _z = -1; private float _lineHeight; private DrawQueue _queue; public DimUI(DimUIConfig config) { _config = config; } [MemberNotNull(nameof(_queue))] public void Begin(Box2d bounds, DrawQueue queue) { _bounds = bounds; _pen = _bounds.Min; _queue = queue; _firstLine = true; _lineHeight = 0; _z = -1; } public void SameLine() { _sameLine = true; } private void Line() { if (!_firstLine && !_sameLine) { _pen = new Vector2(_bounds.Left, _pen.Y + _lineHeight); } else { _firstLine = false; _sameLine = false; } _pen.X += _config.Margin.X; _lineHeight = 0; } private float Z() { return _z += 0.001f; } public void Text(string text) { Line(); SizeF sz = Typesetter.MeasureString(_config.Font, text); float z = Z(); float h = _config.Margin.Y * 2 + sz.Height; _queue.Text(new Vector3(_pen + new Vector2(0, _config.Margin.X), z), _config.TextBrush, text, _config.Font); _lineHeight = Math.Max(_lineHeight, h); _pen.X += sz.Width; } public void DrawBox( Vector2 position, Vector2 size, IBrush fill, IBrush border, float borderWidth, IBrush? shadow, float offset) { float z = Z(); if (shadow != null) { if (offset >= 0) { _queue.Rect(position + new Vector2(offset), position + size + new Vector2(offset + borderWidth), z, shadow); } else { // Inset shadows are draw a bit weirdly. _queue.Rect(position, position + new Vector2(offset, size.Y), z, shadow); _queue.Rect(position + new Vector2(offset, 0), position + new Vector2(size.X - offset, offset), z, shadow); } } _queue.Rect(position, position + size, z, fill, border, borderWidth, BorderKind.Outset); } public bool Button(string label) { Line(); SizeF sz = Typesetter.MeasureString(_config.Font, label); float h = _config.Margin.Y * 2 + _config.Padding.Y * 2 + sz.Height; DrawBox( _pen + new Vector2(0, _config.Margin.Y), new Vector2(sz.Width + 2 * _config.Padding.X, sz.Height + 2 * _config.Padding.Y), _config.ButtonFillBrush, _config.ButtonBorderBrush, _config.ButtonBorderSize, _config.ButtonShadowBrush, _config.ButtonShadowOffset); float z = Z(); _queue.Text(new Vector3(_pen + new Vector2(_config.Padding.X, _config.Margin.Y + _config.Padding.Y), z), _config.TextBrush, label, _config.Font); _lineHeight = Math.Max(_lineHeight, h); _pen.X += sz.Width + 2 * _config.Padding.X; return false; } public bool Input(string placeholder, StringBuilder value) { Line(); IBrush textBrush; string str; if (value.Length == 0) { textBrush = _config.DisabledText; str = placeholder; } else { textBrush = _config.TextBrush; str = value.ToString(); } SizeF sz = Typesetter.MeasureString(_config.Font, str); float h = _config.Margin.Y * 2 + _config.Padding.Y * 2 + sz.Height; DrawBox( _pen + new Vector2(0, _config.Margin.Y), new Vector2(sz.Width + 2 * _config.Padding.X, sz.Height + 2 * _config.Padding.Y), _config.InputFillBrush, _config.InputBorderBrush, _config.InputBorderSize, _config.InputShadowBrush, _config.InputShadowOffset); float z = Z(); _queue.Text(new Vector3(_pen + new Vector2(_config.Padding.X, _config.Margin.Y + _config.Padding.Y), z), textBrush, str, _config.Font); _lineHeight = Math.Max(_lineHeight, h); _pen.X += sz.Width + 2 * _config.Padding.X; return false; } public void BeginMenu() { } public bool MenuItem(string name) { return false; } public void EndMenu() { } public int Id(ReadOnlySpan str) { // Uses the FVN-1A algorithm in 32-bit mode. const int PRIME = 0x01000193; const int BASIS = unchecked((int)0x811c9dc5); int hash = BASIS; for (int i = 0; i < str.Length; i++) { hash ^= str[i] & 0xFF; hash *= PRIME; hash ^= str[i] >> 8; hash *= PRIME; } return hash; } public void Finish() { // TODO: } } }