using System.Diagnostics.CodeAnalysis; using FontAwesome.Sharp; namespace Pf2eViewer.EntityTreeViewer; internal sealed partial class EntityTreeViewerRow : UserControl { public readonly EntityTreeViewerControl ParentControl; public readonly StructureField Item; public int ViewIndex = 0; public Bitmap IconCache { get { if (_iconCache == null) { UpdateCache(); } return _iconCache!; } set => _iconCache = value; } private Bitmap? _iconCache; public List Ladder { get => _ladder; set { _ladder = value; _iconCache = null; } } private List _ladder = []; public override Font Font { get => ParentControl.Font; set => ParentControl.Font = value; } public override Color ForeColor { get => ParentControl.ForeColor; set => ParentControl.ForeColor = value; } public override Color BackColor { get => ParentControl.BackColor; set => ParentControl.BackColor = value; } public int ItemHeight { get => ParentControl.ItemHeight; set => ParentControl.ItemHeight = value; } public int DepthSize { get => ParentControl.ItemHeight; set => ParentControl.ItemHeight = value; } // Value functions public bool HasButton => Item.FieldType == StructureFieldType.Complex; public int Depth => Item.Depth; public int NameWidth => TextRenderer.MeasureText(NameLabel.Text, Font).Width + DepthSize; public EntityTreeViewerRow(EntityTreeViewerControl parent, StructureField item) { ParentControl = parent; Item = item; InitializeComponent(); ApplyItem(); Refresh(); } private void ApplyItem() { TypeLabel.Font = new(Font.FontFamily, Font.Size - 2, FontStyle.Italic); NameLabel.Text = Item.Name; TypeLabel.Text = Item.GetTypeString(); if (TypeLabel.Text != "") { TypeLabel.Text = $@"({TypeLabel.Text})"; } ValueLabel.Text = Item.GetValueString(); TypeLabel.ForeColor = ValueLabel.ForeColor = GetValueColor(); if (Item.FieldType == StructureFieldType.Complex) { ValueLabel.Font = new(Font, FontStyle.Italic); } UpdateCache(); Refresh(); } public new void Refresh() { base.Refresh(); Left = 0; Top = ViewIndex * ParentControl.ItemHeight; Width = ParentControl.ContentPanel.Width - Left - SystemInformation.VerticalScrollBarWidth; Height = ParentControl.ItemHeight; // Name Panel NamePanel.Left = 0; NamePanel.Width = ParentControl.NameWidth - NamePanel.Left; IconPictureBox.Width = IconCache.Width; IconPictureBox.Image = IconCache; NameLabel.Left = IconPictureBox.Width; NameLabel.Width = NamePanel.Width - NameLabel.Left; // Value Panel ValuePanel.Left = NamePanel.Left + NamePanel.Width; ValuePanel.Width = Width - ValuePanel.Left; Size typeSize = TextRenderer.MeasureText(TypeLabel.Text, TypeLabel.Font); TypeLabel.Width = typeSize.Width + 3; } private Color BlendColor(Color color, double rate = 0.5) { return BlendColor(ForeColor, color, rate); } private Color BlendColor(Color color1, Color color2, double rate) { return Color.FromArgb( Mix(color1.A, color2.A), Mix(color1.R, color2.R), Mix(color1.G, color2.G), Mix(color1.B, color2.B) ); int Mix(int a, int b) => Convert.ToInt32(Math.Round(a * rate + b * (1 - rate))); } public Color GetValueColor() { return Item switch { StructureFieldPrimitive primitive => Type.GetTypeCode(primitive.Type) switch { TypeCode.Boolean => BlendColor(Color.DarkGoldenrod), TypeCode.Int32 => BlendColor(Color.Blue), TypeCode.Double => BlendColor(Color.Aqua), TypeCode.String => BlendColor(Color.Green), _ => ForeColor, }, StructureFieldEnum => BlendColor(Color.Red), _ => ForeColor, }; } // Events private void IconPictureBox_Click(object sender, EventArgs e) { if (Item.GetType() != typeof(StructureFieldComplex)) { return; } StructureFieldComplex complex = (StructureFieldComplex)Item; complex.Collapsed = !complex.Collapsed; ParentControl.UpdateView(); } private Bitmap? GetIconImage() { if (Item.GetType() != typeof(StructureFieldComplex)) { return null; } StructureFieldComplex complex = (StructureFieldComplex)Item; int size = Convert.ToInt32(Math.Round(DepthSize * 0.6)); return complex.Collapsed ? IconChar.PlusSquare.ToBitmap(IconFont.Auto, size, ForeColor) : IconChar.MinusSquare.ToBitmap(IconFont.Auto, size, ForeColor); } private void UpdateCache() { int n = Depth; int h = ItemHeight; int w = DepthSize; _iconCache = new(w * (n + 1), h); if (_ladder.Count < Depth) { return; } Graphics g = Graphics.FromImage(_iconCache); Pen pen = new(Color.FromArgb(127, ForeColor.R, ForeColor.G, ForeColor.B), 1); pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; float cy = 0.5f * h; for (int i = 0; i < n; i++) { float cx = (i + 1.5f) * w; switch (_ladder[i]) { case LadderType.Last: g.DrawLine(pen, cx, 0, cx, cy); break; case LadderType.Through: g.DrawLine(pen, cx, 0, cx, h); break; case LadderType.Empty: default: break; } if (i == n - 1) { g.DrawLine(pen, cx, cy, (i + 2) * w, cy); } } Bitmap? icon = GetIconImage(); if (icon is not null) { float cx = (n + 0.5f) * w; g.FillRectangle( new SolidBrush(BackColor), cx - 0.5f * icon.Width - 1, cy - 0.5f * icon.Height - 1, icon.Width + 2, icon.Height + 2 ); g.DrawImage(icon, new PointF(cx - 0.5f * icon.Width, cy - 0.5f * icon.Height)); } g.Save(); } }