236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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<char> 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:
 | |
|         }
 | |
|     }
 | |
| }
 |