mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-21 22:42:54 +08:00
Added STNodeEditor and translated to english
This commit is contained in:
parent
8207da8d70
commit
7eaf984005
144
CodeWalker.WinForms/STNodeEditor/FrmNodePreviewPanel.cs
Normal file
144
CodeWalker.WinForms/STNodeEditor/FrmNodePreviewPanel.cs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
internal class FrmNodePreviewPanel : Form
|
||||||
|
{
|
||||||
|
public Color BorderColor { get; set; }
|
||||||
|
public bool AutoBorderColor { get; set; }
|
||||||
|
|
||||||
|
private bool m_bRight;
|
||||||
|
private Point m_ptHandle;
|
||||||
|
private int m_nHandleSize;
|
||||||
|
private Rectangle m_rect_handle;
|
||||||
|
private Rectangle m_rect_panel;
|
||||||
|
private Rectangle m_rect_exclude;
|
||||||
|
private Region m_region;
|
||||||
|
private Type m_type;
|
||||||
|
private STNode m_node;
|
||||||
|
private STNodeEditor m_editor;
|
||||||
|
private STNodePropertyGrid m_property;
|
||||||
|
|
||||||
|
private Pen m_pen = new Pen(Color.Black);
|
||||||
|
private SolidBrush m_brush = new SolidBrush(Color.Black);
|
||||||
|
private static FrmNodePreviewPanel m_last_frm;
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
|
||||||
|
|
||||||
|
public FrmNodePreviewPanel(Type stNodeType, Point ptHandle, int nHandleSize, bool bRight, STNodeEditor editor, STNodePropertyGrid propertyGrid) {
|
||||||
|
this.SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||||
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||||
|
|
||||||
|
if (m_last_frm != null) m_last_frm.Close();
|
||||||
|
m_last_frm = this;
|
||||||
|
|
||||||
|
m_editor = editor;
|
||||||
|
m_property = propertyGrid;
|
||||||
|
m_editor.Size = new Size(200, 200);
|
||||||
|
m_property.Size = new Size(200, 200);
|
||||||
|
m_editor.Location = new Point(1 + (bRight ? nHandleSize : 0), 1);
|
||||||
|
m_property.Location = new Point(m_editor.Right, 1);
|
||||||
|
m_property.InfoFirstOnDraw = true;
|
||||||
|
this.Controls.Add(m_editor);
|
||||||
|
this.Controls.Add(m_property);
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||||
|
this.Size = new Size(402 + nHandleSize, 202);
|
||||||
|
|
||||||
|
m_type = stNodeType;
|
||||||
|
m_ptHandle = ptHandle;
|
||||||
|
m_nHandleSize = nHandleSize;
|
||||||
|
m_bRight = bRight;
|
||||||
|
|
||||||
|
this.AutoBorderColor = true;
|
||||||
|
this.BorderColor = Color.DodgerBlue;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoad(EventArgs e) {
|
||||||
|
base.OnLoad(e);
|
||||||
|
m_node = (STNode)Activator.CreateInstance(m_type);
|
||||||
|
m_node.Left = 20; m_node.Top = 20;
|
||||||
|
m_editor.Nodes.Add(m_node);
|
||||||
|
m_property.SetNode(m_node);
|
||||||
|
|
||||||
|
m_rect_panel = new Rectangle(0, 0, 402, 202);
|
||||||
|
m_rect_handle = new Rectangle(m_ptHandle.X, m_ptHandle.Y, m_nHandleSize, m_nHandleSize);
|
||||||
|
m_rect_exclude = new Rectangle(0, m_nHandleSize, m_nHandleSize, this.Height - m_nHandleSize);
|
||||||
|
if (m_bRight) {
|
||||||
|
this.Left = m_ptHandle.X;
|
||||||
|
m_rect_panel.X = m_ptHandle.X + m_nHandleSize;
|
||||||
|
} else {
|
||||||
|
this.Left = m_ptHandle.X - this.Width + m_nHandleSize;
|
||||||
|
m_rect_exclude.X = this.Width - m_nHandleSize;
|
||||||
|
m_rect_panel.X = this.Left;
|
||||||
|
}
|
||||||
|
if (m_ptHandle.Y + this.Height > Screen.GetWorkingArea(this).Bottom) {
|
||||||
|
this.Top = m_ptHandle.Y - this.Height + m_nHandleSize;
|
||||||
|
m_rect_exclude.Y -= m_nHandleSize;
|
||||||
|
} else this.Top = m_ptHandle.Y;
|
||||||
|
m_rect_panel.Y = this.Top;
|
||||||
|
m_region = new Region(new Rectangle(Point.Empty, this.Size));
|
||||||
|
m_region.Exclude(m_rect_exclude);
|
||||||
|
using (Graphics g = this.CreateGraphics()) {
|
||||||
|
IntPtr h = m_region.GetHrgn(g);
|
||||||
|
FrmNodePreviewPanel.SetWindowRgn(this.Handle, h, false);
|
||||||
|
m_region.ReleaseHrgn(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.MouseLeave += Event_MouseLeave;
|
||||||
|
m_editor.MouseLeave += Event_MouseLeave;
|
||||||
|
m_property.MouseLeave += Event_MouseLeave;
|
||||||
|
this.BeginInvoke(new MethodInvoker(() => {
|
||||||
|
m_property.Focus();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnClosing(CancelEventArgs e) {
|
||||||
|
base.OnClosing(e);
|
||||||
|
this.Controls.Clear();
|
||||||
|
m_editor.Nodes.Clear();
|
||||||
|
m_editor.MouseLeave -= Event_MouseLeave;
|
||||||
|
m_property.MouseLeave -= Event_MouseLeave;
|
||||||
|
m_last_frm = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event_MouseLeave(object sender, EventArgs e) {
|
||||||
|
Point pt = Control.MousePosition;
|
||||||
|
if (m_rect_panel.Contains(pt) || m_rect_handle.Contains(pt)) return;
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e) {
|
||||||
|
base.OnPaint(e);
|
||||||
|
Graphics g = e.Graphics;
|
||||||
|
m_pen.Color = this.AutoBorderColor ? m_node.TitleColor : this.BorderColor;
|
||||||
|
m_brush.Color = m_pen.Color;
|
||||||
|
g.DrawRectangle(m_pen, 0, 0, this.Width - 1, this.Height - 1);
|
||||||
|
g.FillRectangle(m_brush, m_rect_exclude.X - 1, m_rect_exclude.Y - 1, m_rect_exclude.Width + 2, m_rect_exclude.Height + 2);
|
||||||
|
|
||||||
|
Rectangle rect = this.RectangleToClient(m_rect_handle);
|
||||||
|
rect.Y = (m_nHandleSize - 14) / 2;
|
||||||
|
rect.X += rect.Y + 1;
|
||||||
|
rect.Width = rect.Height = 14;
|
||||||
|
m_pen.Width = 2;
|
||||||
|
g.DrawLine(m_pen, rect.X + 4, rect.Y + 3, rect.X + 10, rect.Y + 3);
|
||||||
|
g.DrawLine(m_pen, rect.X + 4, rect.Y + 6, rect.X + 10, rect.Y + 6);
|
||||||
|
g.DrawLine(m_pen, rect.X + 4, rect.Y + 11, rect.X + 10, rect.Y + 11);
|
||||||
|
g.DrawLine(m_pen, rect.X + 7, rect.Y + 7, rect.X + 7, rect.Y + 10);
|
||||||
|
m_pen.Width = 1;
|
||||||
|
g.DrawRectangle(m_pen, rect.X, rect.Y, rect.Width - 1, rect.Height - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
CodeWalker.WinForms/STNodeEditor/FrmSTNodePropertyInput.cs
Normal file
114
CodeWalker.WinForms/STNodeEditor/FrmSTNodePropertyInput.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
using ST.Library.UI.NodeEditor;
|
||||||
|
|
||||||
|
namespace ST.Library.UI
|
||||||
|
{
|
||||||
|
internal class FrmSTNodePropertyInput : Form
|
||||||
|
{
|
||||||
|
private STNodePropertyDescriptor m_descriptor;
|
||||||
|
private Rectangle m_rect;
|
||||||
|
private Pen m_pen;
|
||||||
|
private SolidBrush m_brush;
|
||||||
|
private TextBox m_tbx;
|
||||||
|
|
||||||
|
public FrmSTNodePropertyInput(STNodePropertyDescriptor descriptor) {
|
||||||
|
this.SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||||
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||||
|
|
||||||
|
m_rect = descriptor.RectangleR;
|
||||||
|
m_descriptor = descriptor;
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||||
|
this.BackColor = descriptor.Control.AutoColor ? descriptor.Node.TitleColor : descriptor.Control.ItemSelectedColor;
|
||||||
|
m_pen = new Pen(descriptor.Control.ForeColor, 1);
|
||||||
|
m_brush = new SolidBrush(this.BackColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoad(EventArgs e) {
|
||||||
|
base.OnLoad(e);
|
||||||
|
Point pt = m_descriptor.Control.PointToScreen(m_rect.Location);
|
||||||
|
pt.Y += m_descriptor.Control.ScrollOffset;
|
||||||
|
this.Location = pt;
|
||||||
|
this.Size = new System.Drawing.Size(m_rect.Width + m_rect.Height, m_rect.Height);
|
||||||
|
|
||||||
|
m_tbx = new TextBox();
|
||||||
|
m_tbx.Font = m_descriptor.Control.Font;
|
||||||
|
m_tbx.ForeColor = m_descriptor.Control.ForeColor;
|
||||||
|
m_tbx.BackColor = Color.FromArgb(255, m_descriptor.Control.ItemValueBackColor);
|
||||||
|
m_tbx.BorderStyle = BorderStyle.None;
|
||||||
|
|
||||||
|
m_tbx.Size = new Size(this.Width - 4 - m_rect.Height, this.Height - 2);
|
||||||
|
m_tbx.Text = m_descriptor.GetStringFromValue();
|
||||||
|
this.Controls.Add(m_tbx);
|
||||||
|
m_tbx.Location = new Point(2, (this.Height - m_tbx.Height) / 2);
|
||||||
|
m_tbx.SelectAll();
|
||||||
|
m_tbx.LostFocus += (s, ea) => this.Close();
|
||||||
|
m_tbx.KeyDown += new KeyEventHandler(tbx_KeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e) {
|
||||||
|
base.OnPaint(e);
|
||||||
|
Graphics g = e.Graphics;
|
||||||
|
m_brush.Color = m_tbx.BackColor;
|
||||||
|
g.FillRectangle(m_brush, 1, 1, this.Width - 2 - m_rect.Height, this.Height - 2);
|
||||||
|
m_brush.Color = m_descriptor.Control.ForeColor;
|
||||||
|
//Enter
|
||||||
|
g.FillPolygon(m_brush, new Point[]{
|
||||||
|
new Point(this.Width - 21, this.Height - 2),
|
||||||
|
new Point(this.Width - 14, this.Height - 2),
|
||||||
|
new Point(this.Width - 14, this.Height - 8)
|
||||||
|
});
|
||||||
|
g.DrawLine(m_pen, this.Width - 14, this.Height - 3, this.Width - 4, this.Height - 3);
|
||||||
|
g.DrawLine(m_pen, this.Width - 4, this.Height - 3, this.Width - 4, 14);
|
||||||
|
g.DrawLine(m_pen, this.Width - 8, 13, this.Width - 4, 13);
|
||||||
|
//----
|
||||||
|
g.DrawLine(m_pen, this.Width - 19, 11, this.Width - 4, 11);
|
||||||
|
//E
|
||||||
|
g.DrawLine(m_pen, this.Width - 19, 3, this.Width - 16, 3);
|
||||||
|
g.DrawLine(m_pen, this.Width - 19, 6, this.Width - 16, 6);
|
||||||
|
g.DrawLine(m_pen, this.Width - 19, 9, this.Width - 16, 9);
|
||||||
|
g.DrawLine(m_pen, this.Width - 19, 3, this.Width - 19, 9);
|
||||||
|
//S
|
||||||
|
g.DrawLine(m_pen, this.Width - 13, 3, this.Width - 10, 3);
|
||||||
|
g.DrawLine(m_pen, this.Width - 13, 6, this.Width - 10, 6);
|
||||||
|
g.DrawLine(m_pen, this.Width - 13, 9, this.Width - 10, 9);
|
||||||
|
g.DrawLine(m_pen, this.Width - 13, 3, this.Width - 13, 6);
|
||||||
|
g.DrawLine(m_pen, this.Width - 10, 6, this.Width - 10, 9);
|
||||||
|
//C
|
||||||
|
g.DrawLine(m_pen, this.Width - 7, 3, this.Width - 4, 3);
|
||||||
|
g.DrawLine(m_pen, this.Width - 7, 9, this.Width - 4, 9);
|
||||||
|
g.DrawLine(m_pen, this.Width - 7, 3, this.Width - 7, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tbx_KeyDown(object sender, KeyEventArgs e) {
|
||||||
|
if (e.KeyCode == Keys.Escape) this.Close();
|
||||||
|
if (e.KeyCode != Keys.Enter) return;
|
||||||
|
try {
|
||||||
|
m_descriptor.SetValue(((TextBox)sender).Text, null);
|
||||||
|
m_descriptor.Control.Invalidate();//add rect;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
m_descriptor.OnSetValueError(ex);
|
||||||
|
}
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// FrmSTNodePropertyInput
|
||||||
|
//
|
||||||
|
this.ClientSize = new System.Drawing.Size(292, 273);
|
||||||
|
this.Name = "FrmSTNodePropertyInput";
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
CodeWalker.WinForms/STNodeEditor/FrmSTNodePropertySelect.cs
Normal file
116
CodeWalker.WinForms/STNodeEditor/FrmSTNodePropertySelect.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
internal class FrmSTNodePropertySelect : Form
|
||||||
|
{
|
||||||
|
private STNodePropertyDescriptor m_descriptor;
|
||||||
|
private int m_nItemHeight = 25;
|
||||||
|
|
||||||
|
private static Type m_t_bool = typeof(bool);
|
||||||
|
private Pen m_pen;
|
||||||
|
private SolidBrush m_brush;
|
||||||
|
private StringFormat m_sf;
|
||||||
|
private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);// Color.FromArgb(255, 40, 40, 40);
|
||||||
|
private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);// Color.FromArgb(255, 50, 50, 50);
|
||||||
|
private object m_item_hover;
|
||||||
|
|
||||||
|
public FrmSTNodePropertySelect(STNodePropertyDescriptor descriptor) {
|
||||||
|
this.SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||||
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||||
|
|
||||||
|
m_descriptor = descriptor;
|
||||||
|
this.Size = descriptor.RectangleR.Size;
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.BackColor = descriptor.Control.BackColor;
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||||
|
m_pen = new Pen(descriptor.Control.AutoColor ? descriptor.Node.TitleColor : descriptor.Control.ItemSelectedColor, 1);
|
||||||
|
m_brush = new SolidBrush(this.BackColor);
|
||||||
|
m_sf = new StringFormat();
|
||||||
|
m_sf.LineAlignment = StringAlignment.Center;
|
||||||
|
m_sf.FormatFlags = StringFormatFlags.NoWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<object> m_lst_item = new List<object>();
|
||||||
|
|
||||||
|
protected override void OnLoad(EventArgs e) {
|
||||||
|
base.OnLoad(e);
|
||||||
|
Point pt = m_descriptor.Control.PointToScreen(m_descriptor.RectangleR.Location);
|
||||||
|
pt.Y += m_descriptor.Control.ScrollOffset;
|
||||||
|
this.Location = pt;
|
||||||
|
if (m_descriptor.PropertyInfo.PropertyType.IsEnum) {
|
||||||
|
foreach (var v in Enum.GetValues(m_descriptor.PropertyInfo.PropertyType)) m_lst_item.Add(v);
|
||||||
|
} else if (m_descriptor.PropertyInfo.PropertyType == m_t_bool) {
|
||||||
|
m_lst_item.Add(true);
|
||||||
|
m_lst_item.Add(false);
|
||||||
|
} else {
|
||||||
|
this.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.Height = m_lst_item.Count * m_nItemHeight;
|
||||||
|
Rectangle rect = Screen.GetWorkingArea(this);
|
||||||
|
if (this.Bottom > rect.Bottom) this.Top -= (this.Bottom - rect.Bottom);
|
||||||
|
this.MouseLeave += (s, ea) => this.Close();
|
||||||
|
this.LostFocus += (s, ea) => this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e) {
|
||||||
|
base.OnPaint(e);
|
||||||
|
Graphics g = e.Graphics;
|
||||||
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
|
||||||
|
Rectangle rect_back = new Rectangle(0, 0, this.Width, m_nItemHeight);
|
||||||
|
Rectangle rect_font = new Rectangle(10, 0, this.Width - 13, m_nItemHeight);
|
||||||
|
int nIndex = 0;
|
||||||
|
string strVal = m_descriptor.GetStringFromValue();
|
||||||
|
foreach (var v in m_lst_item) {
|
||||||
|
m_brush.Color = nIndex++ % 2 == 0 ? m_clr_item_1 : m_clr_item_2;
|
||||||
|
g.FillRectangle(m_brush, rect_back);
|
||||||
|
if (v == m_item_hover) {
|
||||||
|
m_brush.Color = m_descriptor.Control.ItemHoverColor;
|
||||||
|
g.FillRectangle(m_brush, rect_back);
|
||||||
|
}
|
||||||
|
if (v.ToString() == strVal) {
|
||||||
|
m_brush.Color = m_descriptor.Control.ItemSelectedColor;
|
||||||
|
g.FillRectangle(m_brush, 4, rect_back.Top + 10, 5, 5);
|
||||||
|
}
|
||||||
|
m_brush.Color = m_descriptor.Control.ForeColor;
|
||||||
|
g.DrawString(v.ToString(), m_descriptor.Control.Font, m_brush, rect_font, m_sf);
|
||||||
|
rect_back.Y += m_nItemHeight;
|
||||||
|
rect_font.Y += m_nItemHeight;
|
||||||
|
}
|
||||||
|
g.DrawRectangle(m_pen, 0, 0, this.Width - 1, this.Height - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseMove(MouseEventArgs e) {
|
||||||
|
base.OnMouseMove(e);
|
||||||
|
int nIndex = e.Y / m_nItemHeight;
|
||||||
|
if (nIndex < 0 || nIndex >= m_lst_item.Count) return;
|
||||||
|
var item = m_lst_item[e.Y / m_nItemHeight];
|
||||||
|
if (m_item_hover == item) return;
|
||||||
|
m_item_hover = item;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseClick(MouseEventArgs e) {
|
||||||
|
base.OnMouseClick(e);
|
||||||
|
this.Close();
|
||||||
|
int nIndex = e.Y / m_nItemHeight;
|
||||||
|
if (nIndex < 0) return;
|
||||||
|
if (nIndex > m_lst_item.Count) return;
|
||||||
|
try {
|
||||||
|
m_descriptor.SetValue(m_lst_item[nIndex], null);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
m_descriptor.OnSetValueError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1117
CodeWalker.WinForms/STNodeEditor/STNode.cs
Normal file
1117
CodeWalker.WinForms/STNodeEditor/STNode.cs
Normal file
File diff suppressed because it is too large
Load Diff
121
CodeWalker.WinForms/STNodeEditor/STNodeAttribute.cs
Normal file
121
CodeWalker.WinForms/STNodeEditor/STNodeAttribute.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// STNode node characteristics
|
||||||
|
/// Used to describe STNode developer information and some behaviors
|
||||||
|
/// </summary>
|
||||||
|
public class STNodeAttribute : Attribute
|
||||||
|
{
|
||||||
|
private string _Path;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the path that the STNode node expects in the tree control
|
||||||
|
/// </summary>
|
||||||
|
public string Path {
|
||||||
|
get { return _Path; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Author;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the author name of the STNode node
|
||||||
|
/// </summary>
|
||||||
|
public string Author {
|
||||||
|
get { return _Author; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Mail;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the author mailbox of the STNode node
|
||||||
|
/// </summary>
|
||||||
|
public string Mail {
|
||||||
|
get { return _Mail; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Link;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the author link of the STNode node
|
||||||
|
/// </summary>
|
||||||
|
public string Link {
|
||||||
|
get { return _Link; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Description;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the description information of the STNode node
|
||||||
|
/// </summary>
|
||||||
|
public string Description {
|
||||||
|
get { return _Description; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char[] m_ch_splitter = new char[] { '/', '\\' };
|
||||||
|
private static Regex m_reg = new Regex(@"^https?://", RegexOptions.IgnoreCase);
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an STNode property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strPath">expected path</param>
|
||||||
|
public STNodeAttribute(string strPath) : this(strPath, null, null, null, null) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an STNode property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strPath">expected path</param>
|
||||||
|
/// <param name="strDescription">Description</param>
|
||||||
|
public STNodeAttribute(string strPath, string strDescription) : this(strPath, null, null, null, strDescription) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an STNode property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strPath">expected path</param>
|
||||||
|
/// <param name="strAuthor">STNode author name</param>
|
||||||
|
/// <param name="strMail">STNode author mailbox</param>
|
||||||
|
/// <param name="strLink">STNode author link</param>
|
||||||
|
/// <param name="strDescription">STNode node description information</param>
|
||||||
|
public STNodeAttribute(string strPath, string strAuthor, string strMail, string strLink, string strDescription) {
|
||||||
|
if (!string.IsNullOrEmpty(strPath))
|
||||||
|
strPath = strPath.Trim().Trim(m_ch_splitter).Trim();
|
||||||
|
|
||||||
|
this._Path = strPath;
|
||||||
|
|
||||||
|
this._Author = strAuthor;
|
||||||
|
this._Mail = strMail;
|
||||||
|
this._Description = strDescription;
|
||||||
|
if (string.IsNullOrEmpty(strLink) || strLink.Trim() == string.Empty) return;
|
||||||
|
strLink = strLink.Trim();
|
||||||
|
if (m_reg.IsMatch(strLink))
|
||||||
|
this._Link = strLink;
|
||||||
|
else
|
||||||
|
this._Link = "http://" + strLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<Type, MethodInfo> m_dic = new Dictionary<Type, MethodInfo>();
|
||||||
|
/// <summary>
|
||||||
|
/// Get type helper function
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stNodeType">Node Type</param>
|
||||||
|
/// <returns>Function information</returns>
|
||||||
|
public static MethodInfo GetHelpMethod(Type stNodeType) {
|
||||||
|
if (m_dic.ContainsKey(stNodeType)) return m_dic[stNodeType];
|
||||||
|
var mi = stNodeType.GetMethod("ShowHelpInfo");
|
||||||
|
if (mi == null) return null;
|
||||||
|
if (!mi.IsStatic) return null;
|
||||||
|
var ps = mi.GetParameters ();
|
||||||
|
if (ps.Length != 1) return null;
|
||||||
|
if (ps[0].ParameterType != typeof(string)) return null;
|
||||||
|
m_dic.Add(stNodeType, mi);
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute the helper function for the corresponding node type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stNodeType">Node Type</param>
|
||||||
|
public static void ShowHelp(Type stNodeType) {
|
||||||
|
var mi = STNodeAttribute.GetHelpMethod (stNodeType);
|
||||||
|
if (mi == null) return;
|
||||||
|
mi.Invoke(null, new object[] { stNodeType.Module.FullyQualifiedName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
253
CodeWalker.WinForms/STNodeEditor/STNodeCollection.cs
Normal file
253
CodeWalker.WinForms/STNodeEditor/STNodeCollection.cs
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeCollection : IList, ICollection, IEnumerable
|
||||||
|
{
|
||||||
|
private int _Count;
|
||||||
|
public int Count { get { return _Count; } }
|
||||||
|
private STNode[] m_nodes;
|
||||||
|
private STNodeEditor m_owner;
|
||||||
|
|
||||||
|
internal STNodeCollection(STNodeEditor owner) {
|
||||||
|
if (owner == null) throw new ArgumentNullException("Owner cannot be null");
|
||||||
|
m_owner = owner;
|
||||||
|
m_nodes = new STNode[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MoveToEnd(STNode node) {
|
||||||
|
if (this._Count < 1) return;
|
||||||
|
if (m_nodes[this._Count - 1] == node) return;
|
||||||
|
bool bFound = false;
|
||||||
|
for (int i = 0; i < _Count - 1; i++) {
|
||||||
|
if (m_nodes[i] == node) {
|
||||||
|
bFound = true;
|
||||||
|
}
|
||||||
|
if (bFound) m_nodes[i] = m_nodes[i + 1];
|
||||||
|
}
|
||||||
|
m_nodes[this._Count - 1] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(STNode node) {
|
||||||
|
if (node == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
this.EnsureSpace(1);
|
||||||
|
int nIndex = this.IndexOf(node);
|
||||||
|
if (-1 == nIndex) {
|
||||||
|
nIndex = this._Count;
|
||||||
|
node.Owner = m_owner;
|
||||||
|
//node.BuildSize(true, true, false);
|
||||||
|
m_nodes[this._Count++] = node;
|
||||||
|
m_owner.BuildBounds();
|
||||||
|
m_owner.OnNodeAdded(new STNodeEditorEventArgs(node));
|
||||||
|
m_owner.Invalidate();
|
||||||
|
//m_owner.Invalidate(m_owner.CanvasToControl(new Rectangle(node.Left - 5, node.Top - 5, node.Width + 10, node.Height + 10)));
|
||||||
|
//Console.WriteLine(node.Rectangle);
|
||||||
|
}
|
||||||
|
return nIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRange(STNode[] nodes) {
|
||||||
|
if (nodes == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
this.EnsureSpace(nodes.Length);
|
||||||
|
foreach (var n in nodes) {
|
||||||
|
if (n == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
if (-1 == this.IndexOf(n)) {
|
||||||
|
n.Owner = m_owner;
|
||||||
|
m_nodes[this._Count++] = n;
|
||||||
|
}
|
||||||
|
m_owner.OnNodeAdded(new STNodeEditorEventArgs(n));
|
||||||
|
}
|
||||||
|
m_owner.Invalidate();
|
||||||
|
m_owner.BuildBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
for (int i = 0; i < this._Count; i++) {
|
||||||
|
m_nodes[i].Owner = null;
|
||||||
|
foreach (STNodeOption op in m_nodes[i].InputOptions) op.DisConnectionAll();
|
||||||
|
foreach (STNodeOption op in m_nodes[i].OutputOptions) op.DisConnectionAll();
|
||||||
|
m_owner.OnNodeRemoved(new STNodeEditorEventArgs(m_nodes[i]));
|
||||||
|
m_owner.InternalRemoveSelectedNode(m_nodes[i]);
|
||||||
|
}
|
||||||
|
this._Count = 0;
|
||||||
|
m_nodes = new STNode[4];
|
||||||
|
m_owner.SetActiveNode(null);
|
||||||
|
m_owner.BuildBounds();
|
||||||
|
m_owner.ScaleCanvas(1, 0, 0); //The coordinate system returns when there is no node
|
||||||
|
m_owner.MoveCanvas(10, 10, true, CanvasMoveArgs.All);
|
||||||
|
m_owner.Invalidate(); //If the canvas position and zoom are in the initial state, the above two lines of code will not cause the control to redraw
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(STNode node) {
|
||||||
|
return this.IndexOf(node) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(STNode node) {
|
||||||
|
return Array.IndexOf<STNode>(m_nodes, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int nIndex, STNode node) {
|
||||||
|
if (nIndex < 0 || nIndex >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
if (node == null)
|
||||||
|
throw new ArgumentNullException("Insert object cannot be null");
|
||||||
|
this.EnsureSpace(1);
|
||||||
|
for (int i = this._Count; i > nIndex; i--)
|
||||||
|
m_nodes[i] = m_nodes[i - 1];
|
||||||
|
node.Owner = m_owner;
|
||||||
|
m_nodes[nIndex] = node;
|
||||||
|
this._Count++;
|
||||||
|
//node.BuildSize(true, true,false);
|
||||||
|
m_owner.Invalidate();
|
||||||
|
m_owner.BuildBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFixedSize {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(STNode node) {
|
||||||
|
int nIndex = this.IndexOf(node);
|
||||||
|
if (nIndex != -1) this.RemoveAt(nIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int nIndex) {
|
||||||
|
if (nIndex < 0 || nIndex >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
m_nodes[nIndex].Owner = null;
|
||||||
|
m_owner.InternalRemoveSelectedNode(m_nodes[nIndex]);
|
||||||
|
if (m_owner.ActiveNode == m_nodes[nIndex]) m_owner.SetActiveNode(null);
|
||||||
|
m_owner.OnNodeRemoved(new STNodeEditorEventArgs(m_nodes[nIndex]));
|
||||||
|
this._Count--;
|
||||||
|
for (int i = nIndex, Len = this._Count; i < Len; i++)
|
||||||
|
m_nodes[i] = m_nodes[i + 1];
|
||||||
|
if (this._Count == 0) { //The coordinate system returns when there is no node
|
||||||
|
m_owner.ScaleCanvas(1, 0, 0);
|
||||||
|
m_owner.MoveCanvas(10, 10, true, CanvasMoveArgs.All);
|
||||||
|
} else {
|
||||||
|
m_owner.Invalidate();
|
||||||
|
m_owner.BuildBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNode this[int nIndex] {
|
||||||
|
get {
|
||||||
|
if (nIndex < 0 || nIndex >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
return m_nodes[nIndex];
|
||||||
|
}
|
||||||
|
set { throw new InvalidOperationException("No reassignment of elements"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index) {
|
||||||
|
if (array == null)
|
||||||
|
throw new ArgumentNullException("Array cannot be empty");
|
||||||
|
m_nodes.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSynchronized {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SyncRoot {
|
||||||
|
get { return this; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator GetEnumerator() {
|
||||||
|
for (int i = 0, Len = this._Count; i < Len; i++)
|
||||||
|
yield return m_nodes[i];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Check if there is enough space to expand the capacity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elements">Number of elements to be added</param>
|
||||||
|
private void EnsureSpace(int elements) {
|
||||||
|
if (elements + this._Count > m_nodes.Length) {
|
||||||
|
STNode[] arrTemp = new STNode[Math.Max(m_nodes.Length * 2, elements + this._Count)];
|
||||||
|
m_nodes.CopyTo(arrTemp, 0);
|
||||||
|
m_nodes = arrTemp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//============================================================================
|
||||||
|
int IList.Add(object value) {
|
||||||
|
return this.Add((STNode)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Clear() {
|
||||||
|
this.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.Contains(object value) {
|
||||||
|
return this.Contains((STNode)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IList.IndexOf(object value) {
|
||||||
|
return this.IndexOf((STNode)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Insert(int index, object value) {
|
||||||
|
this.Insert(index, (STNode)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.IsFixedSize {
|
||||||
|
get { return this.IsFixedSize; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.IsReadOnly {
|
||||||
|
get { return this.IsReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Remove(object value) {
|
||||||
|
this.Remove((STNode)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.RemoveAt(int index) {
|
||||||
|
this.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IList.this[int index] {
|
||||||
|
get {
|
||||||
|
return this[index];
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this[index] = (STNode)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection.CopyTo(Array array, int index) {
|
||||||
|
this.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection.Count {
|
||||||
|
get { return this._Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection.IsSynchronized {
|
||||||
|
get { return this.IsSynchronized; }
|
||||||
|
}
|
||||||
|
|
||||||
|
object ICollection.SyncRoot {
|
||||||
|
get { return this.SyncRoot; }
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return this.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNode[] ToArray() {
|
||||||
|
STNode[] nodes = new STNode[this._Count];
|
||||||
|
for (int i = 0; i < nodes.Length; i++)
|
||||||
|
nodes[i] = m_nodes[i];
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
319
CodeWalker.WinForms/STNodeEditor/STNodeControl.cs
Normal file
319
CodeWalker.WinForms/STNodeEditor/STNodeControl.cs
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 DebugST@crystal_lz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* time: 2021-01-06
|
||||||
|
* Author: Crystal_lz
|
||||||
|
* blog: st233.com
|
||||||
|
* Github: DebugST.github.io
|
||||||
|
*/
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeControl
|
||||||
|
{
|
||||||
|
private STNode _Owner;
|
||||||
|
|
||||||
|
public STNode Owner {
|
||||||
|
get { return _Owner; }
|
||||||
|
internal set { _Owner = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _Left;
|
||||||
|
|
||||||
|
public int Left {
|
||||||
|
get { return _Left; }
|
||||||
|
set {
|
||||||
|
_Left = value;
|
||||||
|
this.OnMove(EventArgs.Empty);
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _Top;
|
||||||
|
|
||||||
|
public int Top {
|
||||||
|
get { return _Top; }
|
||||||
|
set {
|
||||||
|
_Top = value;
|
||||||
|
this.OnMove(EventArgs.Empty);
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _Width;
|
||||||
|
|
||||||
|
public int Width {
|
||||||
|
get { return _Width; }
|
||||||
|
set {
|
||||||
|
_Width = value;
|
||||||
|
this.OnResize(EventArgs.Empty);
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _Height;
|
||||||
|
|
||||||
|
public int Height {
|
||||||
|
get { return _Height; }
|
||||||
|
set {
|
||||||
|
_Height = value;
|
||||||
|
this.OnResize(EventArgs.Empty);
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Right { get { return this._Left + this._Width; } }
|
||||||
|
|
||||||
|
public int Bottom { get { return this._Top + this._Height; } }
|
||||||
|
|
||||||
|
public Point Location {
|
||||||
|
get { return new Point(this._Left, this._Top); }
|
||||||
|
set {
|
||||||
|
this.Left = value.X;
|
||||||
|
this.Top = value.Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Size Size {
|
||||||
|
get { return new Size(this._Width, this._Height); }
|
||||||
|
set {
|
||||||
|
this.Width = value.Width;
|
||||||
|
this.Height = value.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Rectangle DisplayRectangle {
|
||||||
|
get { return new Rectangle(this._Left, this._Top, this._Width, this._Height); }
|
||||||
|
set {
|
||||||
|
this.Left = value.X;
|
||||||
|
this.Top = value.Y;
|
||||||
|
this.Width = value.Width;
|
||||||
|
this.Height = value.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Rectangle ClientRectangle {
|
||||||
|
get { return new Rectangle(0, 0, this._Width, this._Height); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _BackColor = Color.FromArgb(127, 0, 0, 0);
|
||||||
|
|
||||||
|
public Color BackColor {
|
||||||
|
get { return _BackColor; }
|
||||||
|
set {
|
||||||
|
_BackColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _ForeColor = Color.White;
|
||||||
|
|
||||||
|
public Color ForeColor {
|
||||||
|
get { return _ForeColor; }
|
||||||
|
set {
|
||||||
|
_ForeColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Text = "STNCTRL";
|
||||||
|
|
||||||
|
public string Text {
|
||||||
|
get { return _Text; }
|
||||||
|
set {
|
||||||
|
_Text = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Font _Font;
|
||||||
|
|
||||||
|
public Font Font {
|
||||||
|
get { return _Font; }
|
||||||
|
set {
|
||||||
|
if (value == _Font) return;
|
||||||
|
if (value == null) throw new ArgumentNullException("Value cannot be null");
|
||||||
|
_Font = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _Enabled = true;
|
||||||
|
|
||||||
|
public bool Enabled {
|
||||||
|
get { return _Enabled; }
|
||||||
|
set {
|
||||||
|
if (value == _Enabled) return;
|
||||||
|
_Enabled = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _Visable = true;
|
||||||
|
|
||||||
|
public bool Visable {
|
||||||
|
get { return _Visable; }
|
||||||
|
set {
|
||||||
|
if (value == _Visable) return;
|
||||||
|
_Visable = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected StringFormat m_sf;
|
||||||
|
|
||||||
|
public STNodeControl() {
|
||||||
|
m_sf = new StringFormat();
|
||||||
|
m_sf.Alignment = StringAlignment.Center;
|
||||||
|
m_sf.LineAlignment = StringAlignment.Center;
|
||||||
|
this._Font = new Font("courier new", 8.25f);
|
||||||
|
this.Width = 75;
|
||||||
|
this.Height = 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal virtual void OnPaint(DrawingTools dt) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
SolidBrush brush = dt.SolidBrush;
|
||||||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
||||||
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
|
||||||
|
brush.Color = this._BackColor;
|
||||||
|
g.FillRectangle(brush, 0, 0, this.Width, this.Height);
|
||||||
|
if (!string.IsNullOrEmpty(this._Text)) {
|
||||||
|
brush.Color = this._ForeColor;
|
||||||
|
g.DrawString(this._Text, this._Font, brush, this.ClientRectangle, m_sf);
|
||||||
|
}
|
||||||
|
if (this.Paint != null) this.Paint(this, new STNodeControlPaintEventArgs(dt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invalidate() {
|
||||||
|
if (this._Owner == null) return;
|
||||||
|
this._Owner.Invalidate(new Rectangle(this._Left, this._Top + this._Owner.TitleHeight, this.Width, this.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invalidate(Rectangle rect) {
|
||||||
|
if (this._Owner == null) return;
|
||||||
|
this._Owner.Invalidate(this.RectangleToParent(rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle RectangleToParent(Rectangle rect) {
|
||||||
|
return new Rectangle(this._Left, this._Top + this._Owner.TitleHeight, this.Width, this.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler GotFocus;
|
||||||
|
public event EventHandler LostFocus;
|
||||||
|
public event EventHandler MouseEnter;
|
||||||
|
public event EventHandler MouseLeave;
|
||||||
|
public event MouseEventHandler MouseDown;
|
||||||
|
public event MouseEventHandler MouseMove;
|
||||||
|
public event MouseEventHandler MouseUp;
|
||||||
|
public event MouseEventHandler MouseClick;
|
||||||
|
public event MouseEventHandler MouseWheel;
|
||||||
|
public event EventHandler MouseHWheel;
|
||||||
|
|
||||||
|
public event KeyEventHandler KeyDown;
|
||||||
|
public event KeyEventHandler KeyUp;
|
||||||
|
public event KeyPressEventHandler KeyPress;
|
||||||
|
|
||||||
|
public event EventHandler Move;
|
||||||
|
public event EventHandler Resize;
|
||||||
|
|
||||||
|
public event STNodeControlPaintEventHandler Paint;
|
||||||
|
|
||||||
|
protected internal virtual void OnGotFocus(EventArgs e) {
|
||||||
|
if (this.GotFocus != null) this.GotFocus(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnLostFocus(EventArgs e) {
|
||||||
|
if (this.LostFocus != null) this.LostFocus(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseEnter(EventArgs e) {
|
||||||
|
if (this.MouseEnter != null) this.MouseEnter(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseLeave(EventArgs e) {
|
||||||
|
if (this.MouseLeave != null) this.MouseLeave(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseDown(MouseEventArgs e) {
|
||||||
|
if (this.MouseDown != null) this.MouseDown(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseMove(MouseEventArgs e) {
|
||||||
|
if (this.MouseMove != null) this.MouseMove(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseUp(MouseEventArgs e) {
|
||||||
|
if (this.MouseUp != null) this.MouseUp(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseClick(MouseEventArgs e) {
|
||||||
|
if (this.MouseClick != null) this.MouseClick(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseWheel(MouseEventArgs e) {
|
||||||
|
if (this.MouseWheel != null) this.MouseWheel(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnMouseHWheel(MouseEventArgs e) {
|
||||||
|
if (this.MouseHWheel != null) this.MouseHWheel(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal virtual void OnKeyDown(KeyEventArgs e) {
|
||||||
|
if (this.KeyDown != null) this.KeyDown(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnKeyUp(KeyEventArgs e) {
|
||||||
|
if (this.KeyUp != null) this.KeyUp(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnKeyPress(KeyPressEventArgs e) {
|
||||||
|
if (this.KeyPress != null) this.KeyPress(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal virtual void OnMove(EventArgs e) {
|
||||||
|
if (this.Move != null) this.Move(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal virtual void OnResize(EventArgs e) {
|
||||||
|
if (this.Resize != null) this.Resize(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncResult BeginInvoke(Delegate method) { return this.BeginInvoke(method, null); }
|
||||||
|
public IAsyncResult BeginInvoke(Delegate method, params object[] args) {
|
||||||
|
if (this._Owner == null) return null;
|
||||||
|
return this._Owner.BeginInvoke(method, args);
|
||||||
|
}
|
||||||
|
public object Invoke(Delegate method) { return this.Invoke(method, null); }
|
||||||
|
public object Invoke(Delegate method, params object[] args) {
|
||||||
|
if (this._Owner == null) return null;
|
||||||
|
return this._Owner.Invoke(method, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void STNodeControlPaintEventHandler(object sender, STNodeControlPaintEventArgs e);
|
||||||
|
|
||||||
|
public class STNodeControlPaintEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Drawing tools
|
||||||
|
/// </summary>
|
||||||
|
public DrawingTools DrawingTools { get; private set; }
|
||||||
|
|
||||||
|
public STNodeControlPaintEventArgs(DrawingTools dt) {
|
||||||
|
this.DrawingTools = dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
214
CodeWalker.WinForms/STNodeEditor/STNodeControlCollection.cs
Normal file
214
CodeWalker.WinForms/STNodeEditor/STNodeControlCollection.cs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeControlCollection: IList, ICollection, IEnumerable
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* To ensure security, only inheritors can access the collection in STNode
|
||||||
|
*/
|
||||||
|
private int _Count;
|
||||||
|
public int Count { get { return _Count; } }
|
||||||
|
private STNodeControl[] m_controls;
|
||||||
|
private STNode m_owner;
|
||||||
|
|
||||||
|
internal STNodeControlCollection(STNode owner) {
|
||||||
|
if (owner == null) throw new ArgumentNullException("Owner cannot be null");
|
||||||
|
m_owner = owner;
|
||||||
|
m_controls = new STNodeControl[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(STNodeControl control) {
|
||||||
|
if (control == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
this.EnsureSpace(1);
|
||||||
|
int nIndex = this.IndexOf(control);
|
||||||
|
if (-1 == nIndex) {
|
||||||
|
nIndex = this._Count;
|
||||||
|
control.Owner = m_owner;
|
||||||
|
m_controls[this._Count++] = control;
|
||||||
|
this.Redraw();
|
||||||
|
}
|
||||||
|
return nIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRange(STNodeControl[] controls) {
|
||||||
|
if (controls == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
this.EnsureSpace(controls.Length);
|
||||||
|
foreach (var op in controls) {
|
||||||
|
if (op == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
if (-1 == this.IndexOf(op)) {
|
||||||
|
op.Owner = m_owner;
|
||||||
|
m_controls[this._Count++] = op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
for (int i = 0; i < this._Count; i++) m_controls[i].Owner = null;
|
||||||
|
this._Count = 0;
|
||||||
|
m_controls = new STNodeControl[4];
|
||||||
|
this.Redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(STNodeControl option) {
|
||||||
|
return this.IndexOf(option) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(STNodeControl option) {
|
||||||
|
return Array.IndexOf<STNodeControl>(m_controls, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, STNodeControl control) {
|
||||||
|
if (index < 0 || index >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
if (control == null)
|
||||||
|
throw new ArgumentNullException("Insert object cannot be null");
|
||||||
|
this.EnsureSpace(1);
|
||||||
|
for (int i = this._Count; i > index; i--)
|
||||||
|
m_controls[i] = m_controls[i - 1];
|
||||||
|
control.Owner = m_owner;
|
||||||
|
m_controls[index] = control;
|
||||||
|
this._Count++;
|
||||||
|
this.Redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFixedSize {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(STNodeControl control) {
|
||||||
|
int nIndex = this.IndexOf(control);
|
||||||
|
if (nIndex != -1) this.RemoveAt(nIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index) {
|
||||||
|
if (index < 0 || index >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
this._Count--;
|
||||||
|
m_controls[index].Owner = null;
|
||||||
|
for (int i = index, Len = this._Count; i < Len; i++)
|
||||||
|
m_controls[i] = m_controls[i + 1];
|
||||||
|
this.Redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeControl this[int index] {
|
||||||
|
get {
|
||||||
|
if (index < 0 || index >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
return m_controls[index];
|
||||||
|
}
|
||||||
|
set { throw new InvalidOperationException("No reassignment of elements"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index) {
|
||||||
|
if (array == null)
|
||||||
|
throw new ArgumentNullException("Array cannot be empty");
|
||||||
|
m_controls.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSynchronized {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SyncRoot {
|
||||||
|
get { return this; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator GetEnumerator() {
|
||||||
|
for (int i = 0, Len = this._Count; i < Len; i++)
|
||||||
|
yield return m_controls[i];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Check if there is enough space to expand the capacity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elements">Number of elements to be added</param>
|
||||||
|
private void EnsureSpace(int elements) {
|
||||||
|
if (elements + this._Count > m_controls.Length) {
|
||||||
|
STNodeControl[] arrTemp = new STNodeControl[Math.Max(m_controls.Length * 2, elements + this._Count)];
|
||||||
|
m_controls.CopyTo(arrTemp, 0);
|
||||||
|
m_controls = arrTemp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Redraw() {
|
||||||
|
if (m_owner != null && m_owner.Owner != null) {
|
||||||
|
//m_owner.BuildSize();
|
||||||
|
m_owner.Owner.Invalidate(m_owner.Owner.CanvasToControl(m_owner.Rectangle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//===================================================================================
|
||||||
|
int IList.Add(object value) {
|
||||||
|
return this.Add((STNodeControl)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Clear() {
|
||||||
|
this.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.Contains(object value) {
|
||||||
|
return this.Contains((STNodeControl)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IList.IndexOf(object value) {
|
||||||
|
return this.IndexOf((STNodeControl)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Insert(int index, object value) {
|
||||||
|
this.Insert(index, (STNodeControl)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.IsFixedSize {
|
||||||
|
get { return this.IsFixedSize; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.IsReadOnly {
|
||||||
|
get { return this.IsReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Remove(object value) {
|
||||||
|
this.Remove((STNodeControl)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.RemoveAt(int index) {
|
||||||
|
this.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IList.this[int index] {
|
||||||
|
get {
|
||||||
|
return this[index];
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this[index] = (STNodeControl)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection.CopyTo(Array array, int index) {
|
||||||
|
this.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection.Count {
|
||||||
|
get { return this._Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection.IsSynchronized {
|
||||||
|
get { return this.IsSynchronized; }
|
||||||
|
}
|
||||||
|
|
||||||
|
object ICollection.SyncRoot {
|
||||||
|
get { return this.SyncRoot; }
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return this.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2109
CodeWalker.WinForms/STNodeEditor/STNodeEditor.cs
Normal file
2109
CodeWalker.WinForms/STNodeEditor/STNodeEditor.cs
Normal file
File diff suppressed because it is too large
Load Diff
203
CodeWalker.WinForms/STNodeEditor/STNodeEditorDataType.cs
Normal file
203
CodeWalker.WinForms/STNodeEditor/STNodeEditorDataType.cs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public enum ConnectionStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No owner exists
|
||||||
|
/// </summary>
|
||||||
|
[Description("No owner exists")]
|
||||||
|
NoOwner,
|
||||||
|
/// <summary>
|
||||||
|
/// same owner
|
||||||
|
/// </summary>
|
||||||
|
[Description("same owner")]
|
||||||
|
SameOwner,
|
||||||
|
/// <summary>
|
||||||
|
/// Both are input or output options
|
||||||
|
/// </summary>
|
||||||
|
[Description("both input or output options")]
|
||||||
|
SameInputOrOutput,
|
||||||
|
/// <summary>
|
||||||
|
/// Different data types
|
||||||
|
/// </summary>
|
||||||
|
[Description("Different data types")]
|
||||||
|
ErrorType,
|
||||||
|
/// <summary>
|
||||||
|
/// Single connection node
|
||||||
|
/// </summary>
|
||||||
|
[Description("Single connection node")]
|
||||||
|
SingleOption,
|
||||||
|
/// <summary>
|
||||||
|
/// A circular path appears
|
||||||
|
/// </summary>
|
||||||
|
[Description("A circular path appears")]
|
||||||
|
Loop,
|
||||||
|
/// <summary>
|
||||||
|
/// Existing connection
|
||||||
|
/// </summary>
|
||||||
|
[Description("existing connection")]
|
||||||
|
Exists,
|
||||||
|
/// <summary>
|
||||||
|
/// blank options
|
||||||
|
/// </summary>
|
||||||
|
[Description("Blank option")]
|
||||||
|
EmptyOption,
|
||||||
|
/// <summary>
|
||||||
|
/// already connected
|
||||||
|
/// </summary>
|
||||||
|
[Description("Connected")]
|
||||||
|
Connected,
|
||||||
|
/// <summary>
|
||||||
|
/// The connection is disconnected
|
||||||
|
/// </summary>
|
||||||
|
[Description("The connection was disconnected")]
|
||||||
|
DisConnected,
|
||||||
|
/// <summary>
|
||||||
|
/// Node is locked
|
||||||
|
/// </summary>
|
||||||
|
[Description("Node is locked")]
|
||||||
|
Locked,
|
||||||
|
/// <summary>
|
||||||
|
/// Operation rejected
|
||||||
|
/// </summary>
|
||||||
|
[Description("Operation denied")]
|
||||||
|
Reject,
|
||||||
|
/// <summary>
|
||||||
|
/// is being connected
|
||||||
|
/// </summary>
|
||||||
|
[Description("being connected")]
|
||||||
|
Connecting,
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnecting
|
||||||
|
/// </summary>
|
||||||
|
[Description("Disconnecting")]
|
||||||
|
DisConnecting
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AlertLocation
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Top,
|
||||||
|
Right,
|
||||||
|
Bottom,
|
||||||
|
Center,
|
||||||
|
LeftTop,
|
||||||
|
RightTop,
|
||||||
|
RightBottom,
|
||||||
|
LeftBottom,
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct DrawingTools
|
||||||
|
{
|
||||||
|
public Graphics Graphics;
|
||||||
|
public Pen Pen;
|
||||||
|
public SolidBrush SolidBrush;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CanvasMoveArgs //View the parameters needed when moving the canvas ->MoveCanvas()
|
||||||
|
{
|
||||||
|
Left = 1, //indicates that only the X coordinate is moved
|
||||||
|
Top = 2, //Indicates that only the Y coordinate is moved
|
||||||
|
All = 4 //Indicates that XY move at the same time
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct NodeFindInfo
|
||||||
|
{
|
||||||
|
public STNode Node;
|
||||||
|
public STNodeOption NodeOption;
|
||||||
|
public string Mark;
|
||||||
|
public string[] MarkLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ConnectionInfo
|
||||||
|
{
|
||||||
|
public STNodeOption Input;
|
||||||
|
public STNodeOption Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void STNodeOptionEventHandler(object sender, STNodeOptionEventArgs e);
|
||||||
|
|
||||||
|
public class STNodeOptionEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
private STNodeOption _TargetOption;
|
||||||
|
/// <summary>
|
||||||
|
/// The corresponding Option that triggers this event
|
||||||
|
/// </summary>
|
||||||
|
public STNodeOption TargetOption {
|
||||||
|
get { return _TargetOption; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConnectionStatus _Status;
|
||||||
|
/// <summary>
|
||||||
|
/// Connection status between Option
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionStatus Status {
|
||||||
|
get { return _Status; }
|
||||||
|
internal set { _Status = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _IsSponsor;
|
||||||
|
/// <summary>
|
||||||
|
/// Whether it is the initiator of this behavior
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSponsor {
|
||||||
|
get { return _IsSponsor; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeOptionEventArgs(bool isSponsor, STNodeOption opTarget, ConnectionStatus cr) {
|
||||||
|
this._IsSponsor = isSponsor;
|
||||||
|
this._TargetOption = opTarget;
|
||||||
|
this._Status = cr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void STNodeEditorEventHandler(object sender, STNodeEditorEventArgs e);
|
||||||
|
public delegate void STNodeEditorOptionEventHandler(object sender, STNodeEditorOptionEventArgs e);
|
||||||
|
|
||||||
|
|
||||||
|
public class STNodeEditorEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
private STNode _Node;
|
||||||
|
|
||||||
|
public STNode Node {
|
||||||
|
get { return _Node; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeEditorEventArgs(STNode node) {
|
||||||
|
this._Node = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class STNodeEditorOptionEventArgs : STNodeOptionEventArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
private STNodeOption _CurrentOption;
|
||||||
|
/// <summary>
|
||||||
|
/// Option to actively trigger events
|
||||||
|
/// </summary>
|
||||||
|
public STNodeOption CurrentOption {
|
||||||
|
get { return _CurrentOption; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _Continue = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to continue the downward operation for Begin(Connecting/DisConnecting) whether to continue the backward operation
|
||||||
|
/// </summary>
|
||||||
|
public bool Continue {
|
||||||
|
get { return _Continue; }
|
||||||
|
set { _Continue = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeEditorOptionEventArgs(STNodeOption opTarget, STNodeOption opCurrent, ConnectionStatus cr)
|
||||||
|
: base(false, opTarget, cr) {
|
||||||
|
this._CurrentOption = opCurrent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
329
CodeWalker.WinForms/STNodeEditor/STNodeEditorPanel.cs
Normal file
329
CodeWalker.WinForms/STNodeEditor/STNodeEditorPanel.cs
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeEditorPannel : Control
|
||||||
|
{
|
||||||
|
private bool _LeftLayout = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether it is the left layout
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether it is the left layout"), DefaultValue(true)]
|
||||||
|
public bool LeftLayout {
|
||||||
|
get { return _LeftLayout; }
|
||||||
|
set {
|
||||||
|
if (value == _LeftLayout) return;
|
||||||
|
_LeftLayout = value;
|
||||||
|
this.SetLocation();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _SplitLineColor = Color.Black;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or this is the color of the dividing line
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the color of the dividing line"), DefaultValue(typeof(Color), "Black")]
|
||||||
|
public Color SplitLineColor {
|
||||||
|
get { return _SplitLineColor; }
|
||||||
|
set { _SplitLineColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _HandleLineColor = Color.Gray;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color of the dividing line handle
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the color of the dividing line handle"), DefaultValue(typeof(Color), "Gray")]
|
||||||
|
public Color HandleLineColor {
|
||||||
|
get { return _HandleLineColor; }
|
||||||
|
set { _HandleLineColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _ShowScale = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the display scale when the editor is zoomed
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the display scale when the editor is zoomed"), DefaultValue(true)]
|
||||||
|
public bool ShowScale {
|
||||||
|
get { return _ShowScale; }
|
||||||
|
set { _ShowScale = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _ShowConnectionStatus = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set whether to display the status when the node is connected
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether to display the status when the node is connected"), DefaultValue(true)]
|
||||||
|
public bool ShowConnectionStatus {
|
||||||
|
get { return _ShowConnectionStatus; }
|
||||||
|
set { _ShowConnectionStatus = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _X;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the horizontal width of the dividing line
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the horizontal width of the dividing line"), DefaultValue(201)]
|
||||||
|
public int X {
|
||||||
|
get { return _X; }
|
||||||
|
set {
|
||||||
|
if (value < 122) value = 122;
|
||||||
|
else if (value > this.Width - 122) value = this.Width - 122;
|
||||||
|
if (_X == value) return;
|
||||||
|
_X = value;
|
||||||
|
this.SetLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _Y;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the vertical height of the dividing line
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the vertical height of the dividing line")]
|
||||||
|
public int Y {
|
||||||
|
get { return _Y; }
|
||||||
|
set {
|
||||||
|
if (value < 122) value = 122;
|
||||||
|
else if (value > this.Height - 122) value = this.Height - 122;
|
||||||
|
if (_Y == value) return;
|
||||||
|
_Y = value;
|
||||||
|
this.SetLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the STNodeEditor in the panel
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get the STNodeEditor in the panel"), Browsable(false)]
|
||||||
|
public STNodeEditor Editor {
|
||||||
|
get { return m_editor; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the STNodeTreeView in the panel
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get the STNodeTreeView in the panel"), Browsable(false)]
|
||||||
|
public STNodeTreeView TreeView {
|
||||||
|
get { return m_tree; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the STNodePropertyGrid in the panel
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get the STNodePropertyGrid in the panel"), Browsable(false)]
|
||||||
|
public STNodePropertyGrid PropertyGrid {
|
||||||
|
get { return m_grid; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point m_pt_down;
|
||||||
|
private bool m_is_mx;
|
||||||
|
private bool m_is_my;
|
||||||
|
private Pen m_pen;
|
||||||
|
|
||||||
|
private bool m_nInited;
|
||||||
|
private Dictionary<ConnectionStatus, string> m_dic_status_key = new Dictionary<ConnectionStatus, string>();
|
||||||
|
|
||||||
|
private STNodeEditor m_editor;
|
||||||
|
private STNodeTreeView m_tree;
|
||||||
|
private STNodePropertyGrid m_grid;
|
||||||
|
|
||||||
|
public override Size MinimumSize {
|
||||||
|
get {
|
||||||
|
return base.MinimumSize;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
value = new Size(250, 250);
|
||||||
|
base.MinimumSize = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool bRedraw);
|
||||||
|
|
||||||
|
public STNodeEditorPannel() {
|
||||||
|
this.SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||||
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||||
|
|
||||||
|
m_editor = new STNodeEditor();
|
||||||
|
m_tree = new STNodeTreeView();
|
||||||
|
m_grid = new STNodePropertyGrid();
|
||||||
|
m_grid.Text = "NodeProperty";
|
||||||
|
this.Controls.Add(m_editor);
|
||||||
|
this.Controls.Add(m_tree);
|
||||||
|
this.Controls.Add(m_grid);
|
||||||
|
this.Size = new Size(500, 500);
|
||||||
|
this.MinimumSize = new Size(250, 250);
|
||||||
|
this.BackColor = Color.FromArgb(255, 34, 34, 34);
|
||||||
|
|
||||||
|
m_pen = new Pen(this.BackColor, 3);
|
||||||
|
|
||||||
|
Type t = typeof(ConnectionStatus);
|
||||||
|
var vv = Enum.GetValues(t);
|
||||||
|
var vvv = vv.GetValue(0);
|
||||||
|
foreach (var f in t.GetFields()) {
|
||||||
|
if (!f.FieldType.IsEnum) continue;
|
||||||
|
foreach (var a in f.GetCustomAttributes(true)) {
|
||||||
|
if (!(a is DescriptionAttribute)) continue;
|
||||||
|
m_dic_status_key.Add((ConnectionStatus)f.GetValue(f), ((DescriptionAttribute)a).Description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_editor.ActiveChanged += (s, e) => m_grid.SetNode(m_editor.ActiveNode);
|
||||||
|
m_editor.CanvasScaled += (s, e) => {
|
||||||
|
if (this._ShowScale)
|
||||||
|
m_editor.ShowAlert(m_editor.CanvasScale.ToString("F2"), Color.White, Color.FromArgb(127, 255, 255, 0));
|
||||||
|
};
|
||||||
|
m_editor.OptionConnected += (s, e) => {
|
||||||
|
if (this._ShowConnectionStatus)
|
||||||
|
m_editor.ShowAlert(m_dic_status_key[e.Status], Color.White, e.Status == ConnectionStatus.Connected ? Color.FromArgb(125, Color.Lime) : Color.FromArgb(125, Color.Red));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnResize(EventArgs e) {
|
||||||
|
base.OnResize(e);
|
||||||
|
if (!m_nInited) {
|
||||||
|
this._Y = this.Height / 2;
|
||||||
|
if (this._LeftLayout)
|
||||||
|
this._X = 201;
|
||||||
|
else
|
||||||
|
this._X = this.Width - 202;
|
||||||
|
m_nInited = true;
|
||||||
|
this.SetLocation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.SetLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
|
||||||
|
if (width < 250) width = 250;
|
||||||
|
if (height < 250) height = 250;
|
||||||
|
base.SetBoundsCore(x, y, width, height, specified);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e) {
|
||||||
|
base.OnPaint(e);
|
||||||
|
Graphics g = e.Graphics;
|
||||||
|
m_pen.Width = 3;
|
||||||
|
m_pen.Color = this._SplitLineColor;
|
||||||
|
g.DrawLine(m_pen, this._X, 0, this._X, this.Height);
|
||||||
|
int nX = 0;
|
||||||
|
if (this._LeftLayout) {
|
||||||
|
g.DrawLine(m_pen, 0, this._Y, this._X - 1, this._Y);
|
||||||
|
nX = this._X / 2;
|
||||||
|
} else {
|
||||||
|
g.DrawLine(m_pen, this._X + 2, this._Y, this.Width, this._Y);
|
||||||
|
nX = this._X + (this.Width - this._X) / 2;
|
||||||
|
}
|
||||||
|
m_pen.Width = 1;
|
||||||
|
this._HandleLineColor = Color.Gray;
|
||||||
|
m_pen.Color = this._HandleLineColor;
|
||||||
|
g.DrawLine(m_pen, this._X, this._Y - 10, this._X, this._Y + 10);
|
||||||
|
g.DrawLine(m_pen, nX - 10, this._Y, nX + 10, this._Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetLocation() {
|
||||||
|
if (this._LeftLayout) {
|
||||||
|
//m_tree.Location = Point.Empty;
|
||||||
|
//m_tree.Size = new Size(m_sx - 1, m_sy - 1);
|
||||||
|
STNodeEditorPannel.MoveWindow(m_tree.Handle, 0, 0, this._X - 1, this._Y - 1, false);
|
||||||
|
|
||||||
|
//m_grid.Location = new Point (0, m_sy + 2);
|
||||||
|
//m_grid.Size = new Size(m_sx - 1, this.Height - m_sy - 2);
|
||||||
|
STNodeEditorPannel.MoveWindow(m_grid.Handle, 0, this._Y + 2, this._X - 1, this.Height - this._Y - 2, false);
|
||||||
|
|
||||||
|
//m_editor.Location = new Point(m_sx + 2, 0);
|
||||||
|
//m_editor.Size = new Size(this.Width - m_sx - 2, this.Height);
|
||||||
|
STNodeEditorPannel.MoveWindow(m_editor.Handle, this._X + 2, 0, this.Width - this._X - 2, this.Height, false);
|
||||||
|
} else {
|
||||||
|
STNodeEditorPannel.MoveWindow(m_editor.Handle, 0, 0, this._X - 1, this.Height, false);
|
||||||
|
STNodeEditorPannel.MoveWindow(m_tree.Handle, this._X + 2, 0, this.Width - this._X - 2, this._Y - 1, false);
|
||||||
|
STNodeEditorPannel.MoveWindow(m_grid.Handle, this._X + 2, this._Y + 2, this.Width - this._X - 2, this.Height - this._Y - 2, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseDown(MouseEventArgs e) {
|
||||||
|
base.OnMouseDown(e);
|
||||||
|
m_pt_down = e.Location;
|
||||||
|
m_is_mx = m_is_my = false;
|
||||||
|
if (this.Cursor == Cursors.VSplit) {
|
||||||
|
m_is_mx = true;
|
||||||
|
} else if (this.Cursor == Cursors.HSplit) {
|
||||||
|
m_is_my = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseMove(MouseEventArgs e) {
|
||||||
|
base.OnMouseMove(e);
|
||||||
|
if (e.Button == MouseButtons.Left) {
|
||||||
|
int nw = 122;// (int)(this.Width * 0.1f);
|
||||||
|
int nh = 122;// (int)(this.Height * 0.1f);
|
||||||
|
if (m_is_mx) {
|
||||||
|
this._X = e.X;// -m_pt_down.X;
|
||||||
|
if (this._X < nw) this._X = nw;
|
||||||
|
else if (_X + nw > this.Width) this._X = this.Width - nw;
|
||||||
|
} else if (m_is_my) {
|
||||||
|
this._Y = e.Y;
|
||||||
|
if (this._Y < nh) this._Y = nh;
|
||||||
|
else if (this._Y + nh > this.Height) this._Y = this.Height - nh;
|
||||||
|
}
|
||||||
|
//m_rx = this.Width - m_sx;// (float)m_sx / this.Width;
|
||||||
|
//m_fh = (float)m_sy / this.Height;
|
||||||
|
this.SetLocation();
|
||||||
|
this.Invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Math.Abs(e.X - this._X) < 2)
|
||||||
|
this.Cursor = Cursors.VSplit;
|
||||||
|
else if (Math.Abs(e.Y - this._Y) < 2)
|
||||||
|
this.Cursor = Cursors.HSplit;
|
||||||
|
else this.Cursor = Cursors.Arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseLeave(EventArgs e) {
|
||||||
|
base.OnMouseLeave(e);
|
||||||
|
m_is_mx = m_is_my = false;
|
||||||
|
this.Cursor = Cursors.Arrow;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Add a STNode to the tree control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stNodeType">STNode type</param>
|
||||||
|
/// <returns>Whether the addition is successful</returns>
|
||||||
|
public bool AddSTNode(Type stNodeType) {
|
||||||
|
return m_tree.AddNode(stNodeType);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Load STNode from assembly
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strFileName">Assembly path</param>
|
||||||
|
/// <returns>Add success number</returns>
|
||||||
|
public int LoadAssembly(string strFileName) {
|
||||||
|
m_editor.LoadAssembly(strFileName);
|
||||||
|
return m_tree.LoadAssembly(strFileName);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the text for the editor to display the connection status
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="status">Connection status</param>
|
||||||
|
/// <param name="strText">Corresponding display text</param>
|
||||||
|
/// <returns>Old text</returns>
|
||||||
|
public string SetConnectionStatusText(ConnectionStatus status, string strText) {
|
||||||
|
string strOld = null;
|
||||||
|
if (m_dic_status_key.ContainsKey(status)) {
|
||||||
|
strOld = m_dic_status_key[status];
|
||||||
|
m_dic_status_key[status] = strText;
|
||||||
|
return strOld;
|
||||||
|
}
|
||||||
|
m_dic_status_key.Add(status, strText);
|
||||||
|
return strText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
192
CodeWalker.WinForms/STNodeEditor/STNodeHub.cs
Normal file
192
CodeWalker.WinForms/STNodeEditor/STNodeHub.cs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Drawing;
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 DebugST@crystal_lz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* create: 2021-12-08
|
||||||
|
* modify: 2021-03-02
|
||||||
|
* Author: Crystal_lz
|
||||||
|
* blog: http://st233.com
|
||||||
|
* Gitee: https://gitee.com/DebugST
|
||||||
|
* Github: https://github.com/DebugST
|
||||||
|
*/
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeHub : STNode
|
||||||
|
{
|
||||||
|
private bool m_bSingle;
|
||||||
|
private string m_strIn;
|
||||||
|
private string m_strOut;
|
||||||
|
|
||||||
|
public STNodeHub() : this(false, "IN", "OUT") { }
|
||||||
|
public STNodeHub(bool bSingle) : this(bSingle, "IN", "OUT") { }
|
||||||
|
public STNodeHub(bool bSingle, string strTextIn, string strTextOut) {
|
||||||
|
m_bSingle = bSingle;
|
||||||
|
m_strIn = strTextIn;
|
||||||
|
m_strOut = strTextOut;
|
||||||
|
this.Addhub();
|
||||||
|
this.Title = "HUB";
|
||||||
|
this.AutoSize = false;
|
||||||
|
this.TitleColor = System.Drawing.Color.FromArgb(200, System.Drawing.Color.DarkOrange);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnOwnerChanged() {
|
||||||
|
base.OnOwnerChanged();
|
||||||
|
if (this.Owner == null) return;
|
||||||
|
using (Graphics g = this.Owner.CreateGraphics()) {
|
||||||
|
this.Width = base.GetDefaultNodeSize(g).Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Addhub() {
|
||||||
|
var input = new STNodeHubOption(m_strIn, typeof(object), m_bSingle);
|
||||||
|
var output = new STNodeHubOption(m_strOut, typeof(object), false);
|
||||||
|
this.InputOptions.Add(input);
|
||||||
|
this.OutputOptions.Add(output);
|
||||||
|
input.Connected += new STNodeOptionEventHandler(input_Connected);
|
||||||
|
input.DataTransfer += new STNodeOptionEventHandler(input_DataTransfer);
|
||||||
|
input.DisConnected += new STNodeOptionEventHandler(input_DisConnected);
|
||||||
|
output.Connected += new STNodeOptionEventHandler(output_Connected);
|
||||||
|
output.DisConnected += new STNodeOptionEventHandler(output_DisConnected);
|
||||||
|
this.Height = this.TitleHeight + this.InputOptions.Count * 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_DisConnected(object sender, STNodeOptionEventArgs e) {
|
||||||
|
STNodeOption op = sender as STNodeOption;
|
||||||
|
if (op.ConnectionCount != 0) return;
|
||||||
|
int nIndex = this.OutputOptions.IndexOf(op);
|
||||||
|
if (this.InputOptions[nIndex].ConnectionCount != 0) return;
|
||||||
|
this.InputOptions.RemoveAt(nIndex);
|
||||||
|
this.OutputOptions.RemoveAt(nIndex);
|
||||||
|
if (this.Owner != null) this.Owner.BuildLinePath();
|
||||||
|
this.Height -= 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_Connected(object sender, STNodeOptionEventArgs e) {
|
||||||
|
STNodeOption op = sender as STNodeOption;
|
||||||
|
int nIndex = this.OutputOptions.IndexOf(op);
|
||||||
|
var t = typeof(object);
|
||||||
|
if (this.InputOptions[nIndex].DataType == t) {
|
||||||
|
op.DataType = e.TargetOption.DataType;
|
||||||
|
this.InputOptions[nIndex].DataType = op.DataType;
|
||||||
|
foreach (STNodeOption v in this.InputOptions) {
|
||||||
|
if (v.DataType == t) return;
|
||||||
|
}
|
||||||
|
this.Addhub();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_DisConnected(object sender, STNodeOptionEventArgs e) {
|
||||||
|
STNodeOption op = sender as STNodeOption;
|
||||||
|
if (op.ConnectionCount != 0) return;
|
||||||
|
int nIndex = this.InputOptions.IndexOf(op);
|
||||||
|
if (this.OutputOptions[nIndex].ConnectionCount != 0) return;
|
||||||
|
this.InputOptions.RemoveAt(nIndex);
|
||||||
|
this.OutputOptions.RemoveAt(nIndex);
|
||||||
|
if (this.Owner != null) this.Owner.BuildLinePath();
|
||||||
|
this.Height -= 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_DataTransfer(object sender, STNodeOptionEventArgs e) {
|
||||||
|
STNodeOption op = sender as STNodeOption;
|
||||||
|
int nIndex = this.InputOptions.IndexOf(op);
|
||||||
|
if (e.Status != ConnectionStatus.Connected)
|
||||||
|
this.OutputOptions[nIndex].Data = null;
|
||||||
|
else
|
||||||
|
this.OutputOptions[nIndex].Data = e.TargetOption.Data;
|
||||||
|
this.OutputOptions[nIndex].TransferData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_Connected(object sender, STNodeOptionEventArgs e) {
|
||||||
|
STNodeOption op = sender as STNodeOption;
|
||||||
|
int nIndex = this.InputOptions.IndexOf(op);
|
||||||
|
var t = typeof(object);
|
||||||
|
if (op.DataType == t) {
|
||||||
|
op.DataType = e.TargetOption.DataType;
|
||||||
|
this.OutputOptions[nIndex].DataType = op.DataType;
|
||||||
|
foreach (STNodeOption v in this.InputOptions) {
|
||||||
|
if (v.DataType == t) return;
|
||||||
|
}
|
||||||
|
this.Addhub();
|
||||||
|
} else {
|
||||||
|
//this.OutputOptions[nIndex].Data = e.TargetOption.Data;
|
||||||
|
this.OutputOptions[nIndex].TransferData(e.TargetOption.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSaveNode(Dictionary<string, byte[]> dic) {
|
||||||
|
dic.Add("count", BitConverter.GetBytes(this.InputOptionsCount));
|
||||||
|
//dic.Add("single", new byte[] { (byte)(m_bSingle ? 1 : 0) });
|
||||||
|
//dic.Add("strin", Encoding.UTF8.GetBytes(m_strIn));
|
||||||
|
//dic.Add("strout", Encoding.UTF8.GetBytes(m_strOut));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal override void OnLoadNode(Dictionary<string, byte[]> dic) {
|
||||||
|
base.OnLoadNode(dic);
|
||||||
|
int nCount = BitConverter.ToInt32(dic["count"], 0);
|
||||||
|
while (this.InputOptionsCount < nCount && this.InputOptionsCount != nCount) this.Addhub();
|
||||||
|
//m_bSingle = dic["single"][0] == 1;
|
||||||
|
//m_strIn = Encoding.UTF8.GetString(dic["strin"]);
|
||||||
|
//m_strOut = Encoding.UTF8.GetString(dic["strout"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class STNodeHubOption : STNodeOption
|
||||||
|
{
|
||||||
|
public STNodeHubOption(string strText, Type dataType, bool bSingle) : base(strText, dataType, bSingle) { }
|
||||||
|
|
||||||
|
public override ConnectionStatus ConnectOption(STNodeOption op) {
|
||||||
|
var t = typeof(object);
|
||||||
|
if (this.DataType != t) return base.ConnectOption(op);
|
||||||
|
this.DataType = op.DataType;
|
||||||
|
var ret = base.ConnectOption(op);
|
||||||
|
if (ret != ConnectionStatus.Connected) this.DataType = t;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ConnectionStatus CanConnect(STNodeOption op) {
|
||||||
|
if (op == STNodeOption.Empty) return ConnectionStatus.EmptyOption;
|
||||||
|
if (this.DataType != typeof(object)) return base.CanConnect(op);
|
||||||
|
if (this.IsInput == op.IsInput) return ConnectionStatus.SameInputOrOutput;
|
||||||
|
if (op.Owner == null || this.Owner == null) return ConnectionStatus.NoOwner;
|
||||||
|
if (op.Owner == this.Owner) return ConnectionStatus.SameOwner;
|
||||||
|
if (this.Owner.LockOption || op.Owner.LockOption) return ConnectionStatus.Locked;
|
||||||
|
if (this.IsSingle && m_hs_connected.Count == 1) return ConnectionStatus.SingleOption;
|
||||||
|
if (op.IsInput && STNodeEditor.CanFindNodePath(op.Owner, this.Owner)) return ConnectionStatus.Loop;
|
||||||
|
if (m_hs_connected.Contains(op)) return ConnectionStatus.Exists;
|
||||||
|
if (op.DataType == typeof(object)) return ConnectionStatus.ErrorType;
|
||||||
|
|
||||||
|
if (!this.IsInput) return ConnectionStatus.Connected;
|
||||||
|
foreach (STNodeOption owner_input in this.Owner.InputOptions) {
|
||||||
|
foreach (STNodeOption o in owner_input.ConnectedOption) {
|
||||||
|
if (o == op) return ConnectionStatus.Exists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ConnectionStatus.Connected; ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
456
CodeWalker.WinForms/STNodeEditor/STNodeOption.cs
Normal file
456
CodeWalker.WinForms/STNodeEditor/STNodeOption.cs
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeOption
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public static readonly STNodeOption Empty = new STNodeOption();
|
||||||
|
|
||||||
|
private STNode _Owner;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Node to which the current Option belongs
|
||||||
|
/// </summary>
|
||||||
|
public STNode Owner {
|
||||||
|
get { return _Owner; }
|
||||||
|
internal set {
|
||||||
|
if (value == _Owner) return;
|
||||||
|
if (_Owner != null) this.DisConnectionAll(); //Disconnect all current connections when the owner changes
|
||||||
|
_Owner = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _IsSingle;
|
||||||
|
/// <summary>
|
||||||
|
/// Get whether the current Option can only be connected once
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSingle {
|
||||||
|
get { return _IsSingle; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _IsInput;
|
||||||
|
/// <summary>
|
||||||
|
/// Get whether the current Option is an input option
|
||||||
|
/// </summary>
|
||||||
|
public bool IsInput {
|
||||||
|
get { return _IsInput; }
|
||||||
|
internal set { _IsInput = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _TextColor = Color.White;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current Option text color
|
||||||
|
/// </summary>
|
||||||
|
public Color TextColor {
|
||||||
|
get { return _TextColor; }
|
||||||
|
internal set {
|
||||||
|
if (value == _TextColor) return;
|
||||||
|
_TextColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _DotColor = Color.Transparent;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color of the current Option connection point
|
||||||
|
/// </summary>
|
||||||
|
public Color DotColor {
|
||||||
|
get { return _DotColor; }
|
||||||
|
internal set {
|
||||||
|
if (value == _DotColor) return;
|
||||||
|
_DotColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Text;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current Option display text
|
||||||
|
/// This property cannot be modified when AutoSize is set
|
||||||
|
/// </summary>
|
||||||
|
public string Text {
|
||||||
|
get { return _Text; }
|
||||||
|
internal set {
|
||||||
|
if (value == _Text) return;
|
||||||
|
_Text = value;
|
||||||
|
if (this._Owner == null) return;
|
||||||
|
this._Owner.BuildSize(true, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _DotLeft;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the left coordinate of the current Option connection point
|
||||||
|
/// </summary>
|
||||||
|
public int DotLeft {
|
||||||
|
get { return _DotLeft; }
|
||||||
|
internal set { _DotLeft = value; }
|
||||||
|
}
|
||||||
|
private int _DotTop;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the upper coordinate of the current Option connection point
|
||||||
|
/// </summary>
|
||||||
|
public int DotTop {
|
||||||
|
get { return _DotTop; }
|
||||||
|
internal set { _DotTop = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _DotSize;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the width of the current Option connection point
|
||||||
|
/// </summary>
|
||||||
|
public int DotSize {
|
||||||
|
get { return _DotSize; }
|
||||||
|
protected set { _DotSize = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rectangle _TextRectangle;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current Option text area
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle TextRectangle {
|
||||||
|
get { return _TextRectangle; }
|
||||||
|
internal set { _TextRectangle = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private object _Data;
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the data contained in the current Option
|
||||||
|
/// </summary>
|
||||||
|
public object Data {
|
||||||
|
get { return _Data; }
|
||||||
|
set {
|
||||||
|
if (value != null) {
|
||||||
|
if (this._DataType == null) return;
|
||||||
|
var t = value.GetType();
|
||||||
|
if (t != this._DataType && !t.IsSubclassOf(this._DataType)) {
|
||||||
|
throw new ArgumentException("Invalid data type The data type must be the specified data type or its subclass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_Data = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type _DataType;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current Option data type
|
||||||
|
/// </summary>
|
||||||
|
public Type DataType {
|
||||||
|
get { return _DataType; }
|
||||||
|
internal set { _DataType = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//private Rectangle _DotRectangle;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the area of the current Option connection point
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle DotRectangle {
|
||||||
|
get {
|
||||||
|
return new Rectangle(this._DotLeft, this._DotTop, this._DotSize, this._DotSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current number of Option connected
|
||||||
|
/// </summary>
|
||||||
|
public int ConnectionCount {
|
||||||
|
get { return m_hs_connected.Count; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Option collection that the current Option is connected to
|
||||||
|
/// </summary>
|
||||||
|
internal HashSet<STNodeOption> ConnectedOption {
|
||||||
|
get { return m_hs_connected; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Properties
|
||||||
|
/// <summary>
|
||||||
|
/// Save the points that have been connected
|
||||||
|
/// </summary>
|
||||||
|
protected HashSet<STNodeOption> m_hs_connected;
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
|
private STNodeOption() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strText">Display text</param>
|
||||||
|
/// <param name="dataType">Data Type</param>
|
||||||
|
/// <param name="bSingle">Whether it is a single connection</param>
|
||||||
|
public STNodeOption(string strText, Type dataType, bool bSingle) {
|
||||||
|
if (dataType == null) throw new ArgumentNullException("The specified data type cannot be null");
|
||||||
|
this._DotSize = 10;
|
||||||
|
m_hs_connected = new HashSet<STNodeOption>();
|
||||||
|
this._DataType = dataType;
|
||||||
|
this._Text = strText;
|
||||||
|
this._IsSingle = bSingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Builder
|
||||||
|
|
||||||
|
#region Event
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when connected
|
||||||
|
/// </summary>
|
||||||
|
public event STNodeOptionEventHandler Connected;
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a connection starts happening
|
||||||
|
/// </summary>
|
||||||
|
public event STNodeOptionEventHandler Connecting;
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the connection is disconnected
|
||||||
|
/// </summary>
|
||||||
|
public event STNodeOptionEventHandler DisConnected;
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the connection starts to drop
|
||||||
|
/// </summary>
|
||||||
|
public event STNodeOptionEventHandler DisConnecting;
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when data is passed
|
||||||
|
/// </summary>
|
||||||
|
public event STNodeOptionEventHandler DataTransfer;
|
||||||
|
|
||||||
|
#endregion Event
|
||||||
|
|
||||||
|
#region protected
|
||||||
|
/// <summary>
|
||||||
|
/// Redraw the entire control
|
||||||
|
/// </summary>
|
||||||
|
protected void Invalidate() {
|
||||||
|
if (this._Owner == null) return;
|
||||||
|
this._Owner.Invalidate();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* At first I thought that only input type options should have events because input is passive and output is active
|
||||||
|
* But later found that events are used for output nodes in STNodeHub, for example
|
||||||
|
* Just in case, it is not very problematic to comment the code here. The output option does not register the event, and the same effect
|
||||||
|
*/
|
||||||
|
protected internal virtual void OnConnected(STNodeOptionEventArgs e) {
|
||||||
|
if (this.Connected != null/* && this._IsInput*/) this.Connected(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnConnecting(STNodeOptionEventArgs e) {
|
||||||
|
if (this.Connecting != null) this.Connecting(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnDisConnected(STNodeOptionEventArgs e) {
|
||||||
|
if (this.DisConnected != null/* && this._IsInput*/) this.DisConnected(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnDisConnecting(STNodeOptionEventArgs e) {
|
||||||
|
if (this.DisConnecting != null) this.DisConnecting(this, e);
|
||||||
|
}
|
||||||
|
protected internal virtual void OnDataTransfer(STNodeOptionEventArgs e) {
|
||||||
|
if (this.DataTransfer != null/* && this._IsInput*/) this.DataTransfer(this, e);
|
||||||
|
}
|
||||||
|
protected void STNodeEidtorConnected(STNodeEditorOptionEventArgs e) {
|
||||||
|
if (this._Owner == null) return;
|
||||||
|
if (this._Owner.Owner == null) return;
|
||||||
|
this._Owner.Owner.OnOptionConnected(e);
|
||||||
|
}
|
||||||
|
protected void STNodeEidtorDisConnected(STNodeEditorOptionEventArgs e) {
|
||||||
|
if (this._Owner == null) return;
|
||||||
|
if (this._Owner.Owner == null) return;
|
||||||
|
this._Owner.Owner.OnOptionDisConnected(e);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The current Option starts to connect to the target Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="op">Option to connect</param>
|
||||||
|
/// <returns>Are you allowed to continue the operation?</returns>
|
||||||
|
protected virtual bool ConnectingOption(STNodeOption op) {
|
||||||
|
if (this._Owner == null) return false;
|
||||||
|
if (this._Owner.Owner == null) return false;
|
||||||
|
STNodeEditorOptionEventArgs e = new STNodeEditorOptionEventArgs(op, this, ConnectionStatus.Connecting);
|
||||||
|
this._Owner.Owner.OnOptionConnecting(e);
|
||||||
|
this.OnConnecting(new STNodeOptionEventArgs(true, op, ConnectionStatus.Connecting));
|
||||||
|
op.OnConnecting(new STNodeOptionEventArgs(false, this, ConnectionStatus.Connecting));
|
||||||
|
return e.Continue;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The current Option starts to disconnect the target Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="op">Option to be disconnected</param>
|
||||||
|
/// <returns>Are you allowed to continue the operation?</returns>
|
||||||
|
protected virtual bool DisConnectingOption(STNodeOption op) {
|
||||||
|
if (this._Owner == null) return false;
|
||||||
|
if (this._Owner.Owner == null) return false;
|
||||||
|
STNodeEditorOptionEventArgs e = new STNodeEditorOptionEventArgs(op, this, ConnectionStatus.DisConnecting);
|
||||||
|
this._Owner.Owner.OnOptionDisConnecting(e);
|
||||||
|
this.OnDisConnecting(new STNodeOptionEventArgs(true, op, ConnectionStatus.DisConnecting));
|
||||||
|
op.OnDisConnecting(new STNodeOptionEventArgs(false, this, ConnectionStatus.DisConnecting));
|
||||||
|
return e.Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion protected
|
||||||
|
|
||||||
|
#region public
|
||||||
|
/// <summary>
|
||||||
|
/// The current Option is connected to the target Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="op">Option to connect</param>
|
||||||
|
/// <returns>Connection result</returns>
|
||||||
|
public virtual ConnectionStatus ConnectOption(STNodeOption op) {
|
||||||
|
if (!this.ConnectingOption(op)) {
|
||||||
|
this.STNodeEidtorConnected(new STNodeEditorOptionEventArgs(op, this, ConnectionStatus.Reject));
|
||||||
|
return ConnectionStatus.Reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
var v = this.CanConnect(op);
|
||||||
|
if (v != ConnectionStatus.Connected) {
|
||||||
|
this.STNodeEidtorConnected(new STNodeEditorOptionEventArgs(op, this, v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
v = op.CanConnect(this);
|
||||||
|
if (v != ConnectionStatus.Connected) {
|
||||||
|
this.STNodeEidtorConnected(new STNodeEditorOptionEventArgs(op, this, v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
op.AddConnection(this, false);
|
||||||
|
this.AddConnection(op, true);
|
||||||
|
this.ControlBuildLinePath();
|
||||||
|
|
||||||
|
this.STNodeEidtorConnected(new STNodeEditorOptionEventArgs(op, this, v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether the current Option can connect to the target Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="op">Option to connect</param>
|
||||||
|
/// <returns>Test results</returns>
|
||||||
|
public virtual ConnectionStatus CanConnect(STNodeOption op) {
|
||||||
|
if (this == STNodeOption.Empty || op == STNodeOption.Empty) return ConnectionStatus.EmptyOption;
|
||||||
|
if (this._IsInput == op.IsInput) return ConnectionStatus.SameInputOrOutput;
|
||||||
|
if (op.Owner == null || this._Owner == null) return ConnectionStatus.NoOwner;
|
||||||
|
if (op.Owner == this._Owner) return ConnectionStatus.SameOwner;
|
||||||
|
if (this._Owner.LockOption || op._Owner.LockOption) return ConnectionStatus.Locked;
|
||||||
|
if (this._IsSingle && m_hs_connected.Count == 1) return ConnectionStatus.SingleOption;
|
||||||
|
if (op.IsInput && STNodeEditor.CanFindNodePath(op.Owner, this._Owner)) return ConnectionStatus.Loop;
|
||||||
|
if (m_hs_connected.Contains(op)) return ConnectionStatus.Exists;
|
||||||
|
if (this._IsInput && op._DataType != this._DataType && !op._DataType.IsSubclassOf(this._DataType)) return ConnectionStatus.ErrorType;
|
||||||
|
return ConnectionStatus.Connected;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The current Option disconnects the target Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="op">Option to be disconnected</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual ConnectionStatus DisConnectOption(STNodeOption op) {
|
||||||
|
if (!this.DisConnectingOption(op)) {
|
||||||
|
this.STNodeEidtorDisConnected(new STNodeEditorOptionEventArgs(op, this, ConnectionStatus.Reject));
|
||||||
|
return ConnectionStatus.Reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op.Owner == null) return ConnectionStatus.NoOwner;
|
||||||
|
if (this._Owner == null) return ConnectionStatus.NoOwner;
|
||||||
|
if (op.Owner.LockOption && this._Owner.LockOption) {
|
||||||
|
this.STNodeEidtorDisConnected(new STNodeEditorOptionEventArgs(op, this, ConnectionStatus.Locked));
|
||||||
|
return ConnectionStatus.Locked;
|
||||||
|
}
|
||||||
|
op.RemoveConnection(this, false);
|
||||||
|
this.RemoveConnection(op, true);
|
||||||
|
this.ControlBuildLinePath();
|
||||||
|
|
||||||
|
this.STNodeEidtorDisConnected(new STNodeEditorOptionEventArgs(op, this, ConnectionStatus.DisConnected));
|
||||||
|
return ConnectionStatus.DisConnected;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnect all connections of the current Option
|
||||||
|
/// </summary>
|
||||||
|
public void DisConnectionAll() {
|
||||||
|
if (this._DataType == null) return;
|
||||||
|
var arr = m_hs_connected.ToArray();
|
||||||
|
foreach (var v in arr) {
|
||||||
|
this.DisConnectOption(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Option collection that the current Option is connected to
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>If it is null, it means that there is no owner, otherwise it returns the collection</returns>
|
||||||
|
public List<STNodeOption> GetConnectedOption() {
|
||||||
|
if (this._DataType == null) return null;
|
||||||
|
if (!this._IsInput)
|
||||||
|
return m_hs_connected.ToList();
|
||||||
|
List<STNodeOption> lst = new List<STNodeOption>();
|
||||||
|
if (this._Owner == null) return null;
|
||||||
|
if (this._Owner.Owner == null) return null;
|
||||||
|
foreach (var v in this._Owner.Owner.GetConnectionInfo()) {
|
||||||
|
if (v.Output == this) lst.Add(v.Input);
|
||||||
|
}
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Post data to all Option connected to the current Option
|
||||||
|
/// </summary>
|
||||||
|
public void TransferData() {
|
||||||
|
if (this._DataType == null) return;
|
||||||
|
foreach (var v in m_hs_connected) {
|
||||||
|
v.OnDataTransfer(new STNodeOptionEventArgs(true, this, ConnectionStatus.Connected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Post data to all Option connected to the current Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to be delivered</param>
|
||||||
|
public void TransferData(object data) {
|
||||||
|
if (this._DataType == null) return;
|
||||||
|
this.Data = data; //not this._Data
|
||||||
|
foreach (var v in m_hs_connected) {
|
||||||
|
v.OnDataTransfer(new STNodeOptionEventArgs(true, this, ConnectionStatus.Connected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Post data to all Option connected to the current Option
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to be delivered</param>
|
||||||
|
/// <param name="bDisposeOld">Whether to release old data</param>
|
||||||
|
public void TransferData(object data, bool bDisposeOld) {
|
||||||
|
if (bDisposeOld && this._Data != null) {
|
||||||
|
if (this._Data is IDisposable) ((IDisposable)this._Data).Dispose();
|
||||||
|
this._Data = null;
|
||||||
|
}
|
||||||
|
this.TransferData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion public
|
||||||
|
|
||||||
|
#region internal
|
||||||
|
|
||||||
|
private bool AddConnection(STNodeOption op, bool bSponsor) {
|
||||||
|
if (this._DataType == null) return false;
|
||||||
|
bool b = m_hs_connected.Add(op);
|
||||||
|
this.OnConnected(new STNodeOptionEventArgs(bSponsor, op, ConnectionStatus.Connected));
|
||||||
|
if (this._IsInput) this.OnDataTransfer(new STNodeOptionEventArgs(bSponsor, op, ConnectionStatus.Connected));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RemoveConnection(STNodeOption op, bool bSponsor) {
|
||||||
|
if (this._DataType == null) return false;
|
||||||
|
bool b = false;
|
||||||
|
if (m_hs_connected.Contains(op)) {
|
||||||
|
b = m_hs_connected.Remove(op);
|
||||||
|
if (this._IsInput) this.OnDataTransfer(new STNodeOptionEventArgs(bSponsor, op, ConnectionStatus.DisConnected));
|
||||||
|
this.OnDisConnected(new STNodeOptionEventArgs(bSponsor, op, ConnectionStatus.Connected));
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion internal
|
||||||
|
|
||||||
|
#region private
|
||||||
|
|
||||||
|
private void ControlBuildLinePath() {
|
||||||
|
if (this.Owner == null) return;
|
||||||
|
if (this.Owner.Owner == null) return;
|
||||||
|
this.Owner.Owner.BuildLinePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
238
CodeWalker.WinForms/STNodeEditor/STNodeOptionCollection.cs
Normal file
238
CodeWalker.WinForms/STNodeEditor/STNodeOptionCollection.cs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeOptionCollection : IList, ICollection, IEnumerable
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Although the collection provides a complete data interface such as: Add,Remove,...
|
||||||
|
* But try not to use some removal operations such as: Remove,RemoveAt,Clear,this[index] = value,...
|
||||||
|
* Because the Owner of each Option is strictly bound in my definition, some operations such as removal or replacement will affect the change of the Owner
|
||||||
|
* So all the original connections will be disconnected and the DisConnect event will be triggered
|
||||||
|
* To ensure security, only inheritors can access the collection in STNode
|
||||||
|
*/
|
||||||
|
private int _Count;
|
||||||
|
public int Count { get { return _Count; } }
|
||||||
|
private STNodeOption[] m_options;
|
||||||
|
private STNode m_owner;
|
||||||
|
|
||||||
|
private bool m_isInput; //Whether the current collection stores the input point
|
||||||
|
|
||||||
|
internal STNodeOptionCollection(STNode owner, bool isInput) {
|
||||||
|
if (owner == null) throw new ArgumentNullException("Owner cannot be null");
|
||||||
|
m_owner = owner;
|
||||||
|
m_isInput = isInput;
|
||||||
|
m_options = new STNodeOption[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeOption Add(string strText, Type dataType, bool bSingle) {
|
||||||
|
//not do this code -> out of bounds
|
||||||
|
//return m_options[this.Add(new STNodeOption(strText, dataType, bSingle))];
|
||||||
|
int nIndex = this.Add(new STNodeOption(strText, dataType, bSingle));
|
||||||
|
return m_options[nIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(STNodeOption option) {
|
||||||
|
if (option == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
this.EnsureSpace(1);
|
||||||
|
int nIndex = option == STNodeOption.Empty ? -1 : this.IndexOf(option);
|
||||||
|
if (-1 == nIndex) {
|
||||||
|
nIndex = this._Count;
|
||||||
|
option.Owner = m_owner;
|
||||||
|
option.IsInput = m_isInput;
|
||||||
|
m_options[this._Count++] = option;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
return nIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRange(STNodeOption[] options) {
|
||||||
|
if (options == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
this.EnsureSpace(options.Length);
|
||||||
|
foreach (var op in options) {
|
||||||
|
if (op == null) throw new ArgumentNullException("Add object cannot be null");
|
||||||
|
if (-1 == this.IndexOf(op)) {
|
||||||
|
op.Owner = m_owner;
|
||||||
|
op.IsInput = m_isInput;
|
||||||
|
m_options[this._Count++] = op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
for (int i = 0; i < this._Count; i++) m_options[i].Owner = null;
|
||||||
|
this._Count = 0;
|
||||||
|
m_options = new STNodeOption[4];
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(STNodeOption option) {
|
||||||
|
return this.IndexOf(option) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(STNodeOption option) {
|
||||||
|
return Array.IndexOf<STNodeOption>(m_options, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, STNodeOption option) {
|
||||||
|
if (index < 0 || index >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
if (option == null)
|
||||||
|
throw new ArgumentNullException("Insert object cannot be null");
|
||||||
|
this.EnsureSpace(1);
|
||||||
|
for (int i = this._Count; i > index; i--)
|
||||||
|
m_options[i] = m_options[i - 1];
|
||||||
|
option.Owner = m_owner;
|
||||||
|
m_options[index] = option;
|
||||||
|
this._Count++;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFixedSize {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(STNodeOption option) {
|
||||||
|
int nIndex = this.IndexOf(option);
|
||||||
|
if (nIndex != -1) this.RemoveAt(nIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index) {
|
||||||
|
if (index < 0 || index >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
this._Count--;
|
||||||
|
m_options[index].Owner = null;
|
||||||
|
for (int i = index, Len = this._Count; i < Len; i++)
|
||||||
|
m_options[i] = m_options[i + 1];
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeOption this[int index] {
|
||||||
|
get {
|
||||||
|
if (index < 0 || index >= this._Count)
|
||||||
|
throw new IndexOutOfRangeException("Index out of bounds");
|
||||||
|
return m_options[index];
|
||||||
|
}
|
||||||
|
set { throw new InvalidOperationException("No reassignment of elements"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index) {
|
||||||
|
if (array == null)
|
||||||
|
throw new ArgumentNullException("Array cannot be empty");
|
||||||
|
m_options.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSynchronized {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SyncRoot {
|
||||||
|
get { return this; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator GetEnumerator() {
|
||||||
|
for (int i = 0, Len = this._Count; i < Len; i++)
|
||||||
|
yield return m_options[i];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Check if there is enough space to expand the capacity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elements">Number of elements to be added</param>
|
||||||
|
private void EnsureSpace(int elements) {
|
||||||
|
if (elements + this._Count > m_options.Length) {
|
||||||
|
STNodeOption[] arrTemp = new STNodeOption[Math.Max(m_options.Length * 2, elements + this._Count)];
|
||||||
|
m_options.CopyTo(arrTemp, 0);
|
||||||
|
m_options = arrTemp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Invalidate() {
|
||||||
|
if (m_owner != null && m_owner.Owner != null) {
|
||||||
|
m_owner.BuildSize(true, true, true);
|
||||||
|
//m_owner.Invalidate();//.Owner.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//===================================================================================
|
||||||
|
int IList.Add(object value) {
|
||||||
|
return this.Add((STNodeOption)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Clear() {
|
||||||
|
this.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.Contains(object value) {
|
||||||
|
return this.Contains((STNodeOption)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IList.IndexOf(object value) {
|
||||||
|
return this.IndexOf((STNodeOption)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Insert(int index, object value) {
|
||||||
|
this.Insert(index, (STNodeOption)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.IsFixedSize {
|
||||||
|
get { return this.IsFixedSize; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IList.IsReadOnly {
|
||||||
|
get { return this.IsReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.Remove(object value) {
|
||||||
|
this.Remove((STNodeOption)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList.RemoveAt(int index) {
|
||||||
|
this.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IList.this[int index] {
|
||||||
|
get {
|
||||||
|
return this[index];
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this[index] = (STNodeOption)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection.CopyTo(Array array, int index) {
|
||||||
|
this.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection.Count {
|
||||||
|
get { return this._Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection.IsSynchronized {
|
||||||
|
get { return this.IsSynchronized; }
|
||||||
|
}
|
||||||
|
|
||||||
|
object ICollection.SyncRoot {
|
||||||
|
get { return this.SyncRoot; }
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return this.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public STNodeOption[] ToArray() {
|
||||||
|
STNodeOption[] ops = new STNodeOption[this._Count];
|
||||||
|
for (int i = 0; i < ops.Length; i++)
|
||||||
|
ops[i] = m_options[i];
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
334
CodeWalker.WinForms/STNodeEditor/STNodePropertyAttribute.cs
Normal file
334
CodeWalker.WinForms/STNodeEditor/STNodePropertyAttribute.cs
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// STNode node attribute characteristics
|
||||||
|
/// Used to describe STNode node attribute information and behavior on the attribute editor
|
||||||
|
/// </summary>
|
||||||
|
public class STNodePropertyAttribute : Attribute
|
||||||
|
{
|
||||||
|
private string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the name of the property that needs to be displayed on the property editor
|
||||||
|
/// </summary>
|
||||||
|
public string Name {
|
||||||
|
get { return _Name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _Description;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the description of the property that needs to be displayed on the property editor
|
||||||
|
/// </summary>
|
||||||
|
public string Description {
|
||||||
|
get { return _Description; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type _ConverterType = typeof(STNodePropertyDescriptor);
|
||||||
|
/// <summary>
|
||||||
|
/// Get the property descriptor type
|
||||||
|
/// </summary>
|
||||||
|
public Type DescriptorType {
|
||||||
|
get { return _ConverterType; }
|
||||||
|
set { _ConverterType = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an STNode property attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strKey">Name to be displayed</param>
|
||||||
|
/// <param name="strDesc">Description information to be displayed</param>
|
||||||
|
public STNodePropertyAttribute(string strKey, string strDesc) {
|
||||||
|
this._Name = strKey;
|
||||||
|
this._Description = strDesc;
|
||||||
|
}
|
||||||
|
//private Type m_descriptor_type_base = typeof(STNodePropertyDescriptor);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// STNode property descriptor
|
||||||
|
/// Used to determine how to interact with the property's value on the property editor and to determine how the property's value will be drawn and interacted on the property editor
|
||||||
|
/// </summary>
|
||||||
|
public class STNodePropertyDescriptor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the target node
|
||||||
|
/// </summary>
|
||||||
|
public STNode Node { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the node attribute editor control to which it belongs
|
||||||
|
/// </summary>
|
||||||
|
public STNodePropertyGrid Control { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the area where the option is located
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle Rectangle { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the area where the option name is located
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle RectangleL { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the area where the option value is located
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle RectangleR { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the name of the option that needs to be displayed
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the description information corresponding to the attribute
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get attribute information
|
||||||
|
/// </summary>
|
||||||
|
public PropertyInfo PropertyInfo { get; internal set; }
|
||||||
|
|
||||||
|
private static Type m_t_int = typeof(int);
|
||||||
|
private static Type m_t_float = typeof(float);
|
||||||
|
private static Type m_t_double = typeof(double);
|
||||||
|
private static Type m_t_string = typeof(string);
|
||||||
|
private static Type m_t_bool = typeof(bool);
|
||||||
|
|
||||||
|
private StringFormat m_sf;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a descriptor
|
||||||
|
/// </summary>
|
||||||
|
public STNodePropertyDescriptor() {
|
||||||
|
m_sf = new StringFormat();
|
||||||
|
m_sf.LineAlignment = StringAlignment.Center;
|
||||||
|
m_sf.FormatFlags = StringFormatFlags.NoWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when determining the position of the STNode property on the property editor
|
||||||
|
/// </summary>
|
||||||
|
protected internal virtual void OnSetItemLocation() { }
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the property value in the form of a string to the value of the property target type
|
||||||
|
/// By default, only int float double string bool and Array of the above types are supported
|
||||||
|
/// If the target type is not in the above, please rewrite this function to convert it yourself
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strText">Attribute value in string form</param>
|
||||||
|
/// <returns>The value of the real target type of the attribute</returns>
|
||||||
|
protected internal virtual object GetValueFromString(string strText) {
|
||||||
|
Type t = this.PropertyInfo.PropertyType;
|
||||||
|
if (t == m_t_int) return int.Parse(strText);
|
||||||
|
if (t == m_t_float) return float.Parse(strText);
|
||||||
|
if (t == m_t_double) return double.Parse(strText);
|
||||||
|
if (t == m_t_string) return strText;
|
||||||
|
if (t == m_t_bool) return bool.Parse(strText);
|
||||||
|
if (t.IsEnum) {
|
||||||
|
return Enum.Parse(t, strText);
|
||||||
|
} else if (t.IsArray) {
|
||||||
|
var t_1 = t.GetElementType();
|
||||||
|
if (t_1 == m_t_string) return strText.Split(',');
|
||||||
|
string[] strs = strText.Trim(new char[] { ' ', ',' }).Split(',');//add other place trim()
|
||||||
|
if (t_1 == m_t_int) {
|
||||||
|
int[] arr = new int[strs.Length];
|
||||||
|
for (int i = 0; i < strs.Length; i++) arr[i] = int.Parse(strs[i].Trim());
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
if (t_1 == m_t_float) {
|
||||||
|
float[] arr = new float[strs.Length];
|
||||||
|
for (int i = 0; i < strs.Length; i++) arr[i] = float.Parse(strs[i].Trim());
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
if (t_1 == m_t_int) {
|
||||||
|
double[] arr = new double[strs.Length];
|
||||||
|
for (int i = 0; i < strs.Length; i++) arr[i] = double.Parse(strs[i].Trim());
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
if (t_1 == m_t_int) {
|
||||||
|
bool[] arr = new bool[strs.Length];
|
||||||
|
for (int i = 0; i < strs.Length; i++) arr[i] = bool.Parse(strs[i].Trim());
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InvalidCastException("Unable to complete the conversion of [string] to [" + t.FullName + "], please overload [STNodePropertyDescriptor.GetValueFromString(string)]");
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the value of the attribute target type to a value in the form of a string
|
||||||
|
/// ToString() operation on type value by default
|
||||||
|
/// If you need special processing, please rewrite this function to convert it yourself
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>String form of attribute value</returns>
|
||||||
|
protected internal virtual string GetStringFromValue() {
|
||||||
|
var v = this.PropertyInfo.GetValue(this.Node, null);
|
||||||
|
var t = this.PropertyInfo.PropertyType;
|
||||||
|
if (v == null) return null;
|
||||||
|
if (t.IsArray) {
|
||||||
|
List<string> lst = new List<string>();
|
||||||
|
foreach (var item in (Array)v) lst.Add(item.ToString());
|
||||||
|
return string.Join(",", lst.ToArray());
|
||||||
|
}
|
||||||
|
return v.ToString();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the attribute value in binary form to the value of the attribute target type for restoring the attribute value from the data in the file store
|
||||||
|
/// Convert it to a string by default and then call GetValueFromString(string)
|
||||||
|
/// This function corresponds to GetBytesFromValue(). If the function needs to be rewritten, the two functions should be rewritten together
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="byData">Binary data</param>
|
||||||
|
/// <returns>The value of the real target type of the attribute</returns>
|
||||||
|
protected internal virtual object GetValueFromBytes(byte[] byData) {
|
||||||
|
if (byData == null) return null;
|
||||||
|
string strText = Encoding.UTF8.GetString(byData);
|
||||||
|
return this.GetValueFromString(strText);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Called when converting the value of the attribute target type to a binary value for file storage
|
||||||
|
/// Calls GetStringFromValue() by default and converts the string to binary data
|
||||||
|
/// For special handling, please rewrite this function to convert it yourself and rewrite GetValueFromBytes()
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Binary form of attribute value</returns>
|
||||||
|
protected internal virtual byte[] GetBytesFromValue() {
|
||||||
|
string strText = this.GetStringFromValue();
|
||||||
|
if (strText == null) return null;
|
||||||
|
return Encoding.UTF8.GetBytes(strText);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.GetValue()
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The optional index value of the indexed attribute should be null for non-indexed attributes</param>
|
||||||
|
/// <returns>Attribute value</returns>
|
||||||
|
protected internal virtual object GetValue(object[] index) {
|
||||||
|
return this.PropertyInfo.GetValue(this.Node, index);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.SetValue()
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Attribute value to be set</param>
|
||||||
|
protected internal virtual void SetValue(object value) {
|
||||||
|
this.PropertyInfo.SetValue(this.Node, value, null);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.SetValue()
|
||||||
|
/// GetValueFromString(strValue) will be processed by default before calling
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strValue">The value of the attribute string that needs to be set</param>
|
||||||
|
protected internal virtual void SetValue(string strValue) {
|
||||||
|
this.PropertyInfo.SetValue(this.Node, this.GetValueFromString(strValue), null);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.SetValue()
|
||||||
|
/// GetValueFromBytes(byte[]) will be processed by default before calling
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="byData">Attribute binary data to be set</param>
|
||||||
|
protected internal virtual void SetValue(byte[] byData) {
|
||||||
|
this.PropertyInfo.SetValue(this.Node, this.GetValueFromBytes(byData), null);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.SetValue()
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Attribute value to be set</param>
|
||||||
|
/// <param name="index">The optional index value of the indexed attribute should be null for non-indexed attributes</param>
|
||||||
|
protected internal virtual void SetValue(object value, object[] index) {
|
||||||
|
this.PropertyInfo.SetValue(this.Node, value, index);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.SetValue()
|
||||||
|
/// GetValueFromString(strValue) will be processed by default before calling
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strValue">The value of the attribute string that needs to be set</param>
|
||||||
|
/// <param name="index">The optional index value of the indexed attribute should be null for non-indexed attributes</param>
|
||||||
|
protected internal virtual void SetValue(string strValue, object[] index) {
|
||||||
|
this.PropertyInfo.SetValue(this.Node, this.GetValueFromString(strValue), index);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This function corresponds to System.Reflection.PropertyInfo.SetValue()
|
||||||
|
/// GetValueFromBytes(byte[]) will be processed by default before calling
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="byData">Attribute binary data to be set</param>
|
||||||
|
/// <param name="index">The optional index value of the indexed attribute should be null for non-indexed attributes</param>
|
||||||
|
protected internal virtual void SetValue(byte[] byData, object[] index) {
|
||||||
|
this.PropertyInfo.SetValue(this.Node, this.GetValueFromBytes(byData), index);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when there is an error setting the property value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">Exception information</param>
|
||||||
|
protected internal virtual void OnSetValueError(Exception ex) {
|
||||||
|
this.Control.SetErrorMessage(ex.Message);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when drawing the property's value area on the property editor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
protected internal virtual void OnDrawValueRectangle(DrawingTools dt) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
SolidBrush brush = dt.SolidBrush;
|
||||||
|
STNodePropertyGrid ctrl = this.Control;
|
||||||
|
//STNodePropertyItem item = this._PropertyItem;
|
||||||
|
brush.Color = ctrl.ItemValueBackColor;
|
||||||
|
|
||||||
|
g.FillRectangle(brush, this.RectangleR);
|
||||||
|
Rectangle rect = this.RectangleR;
|
||||||
|
rect.Width--; rect.Height--;
|
||||||
|
brush.Color = this.Control.ForeColor;
|
||||||
|
g.DrawString(this.GetStringFromValue(), ctrl.Font, brush, this.RectangleR, m_sf);
|
||||||
|
|
||||||
|
if (this.PropertyInfo.PropertyType.IsEnum || this.PropertyInfo.PropertyType == m_t_bool) {
|
||||||
|
g.FillPolygon(Brushes.Gray, new Point[]{
|
||||||
|
new Point(rect.Right - 13, rect.Top + rect.Height / 2 - 2),
|
||||||
|
new Point(rect.Right - 4, rect.Top + rect.Height / 2 - 2),
|
||||||
|
new Point(rect.Right - 9, rect.Top + rect.Height / 2 + 3)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse enters the area where the property value is located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected internal virtual void OnMouseEnter(EventArgs e) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is clicked in the area where the property value is located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected internal virtual void OnMouseDown(MouseEventArgs e) {
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse moves in the area where the property value is located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected internal virtual void OnMouseMove(MouseEventArgs e) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is raised in the area where the property value is located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected internal virtual void OnMouseUp(MouseEventArgs e) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse leaves the area where the property value is located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected internal virtual void OnMouseLeave(EventArgs e) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is clicked in the area where the property value is located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected internal virtual void OnMouseClick(MouseEventArgs e) {
|
||||||
|
Type t = this.PropertyInfo.PropertyType;
|
||||||
|
if (t == m_t_bool || t.IsEnum) {
|
||||||
|
new FrmSTNodePropertySelect(this).Show(this.Control);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Rectangle rect = this.Control.RectangleToScreen(this.RectangleR);
|
||||||
|
new FrmSTNodePropertyInput(this).Show(this.Control);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Redraw the options area
|
||||||
|
/// </summary>
|
||||||
|
public void Invalidate() {
|
||||||
|
Rectangle rect = this.Rectangle;
|
||||||
|
rect.X -= this.Control.ScrollOffset;
|
||||||
|
this.Control.Invalidate(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
860
CodeWalker.WinForms/STNodeEditor/STNodePropertyGrid.cs
Normal file
860
CodeWalker.WinForms/STNodeEditor/STNodePropertyGrid.cs
Normal file
@ -0,0 +1,860 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.ComponentModel;
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 DebugST@crystal_lz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* create: 2021-01-28
|
||||||
|
* modify: 2021-03-02
|
||||||
|
* Author: Crystal_lz
|
||||||
|
* blog: http://st233.com
|
||||||
|
* Gitee: https://gitee.com/DebugST
|
||||||
|
* Github: https://github.com/DebugST
|
||||||
|
*/
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// STNode node attribute editor
|
||||||
|
/// </summary>
|
||||||
|
public class STNodePropertyGrid : Control
|
||||||
|
{
|
||||||
|
#region properties ==========
|
||||||
|
|
||||||
|
private STNode _STNode;
|
||||||
|
/// <summary>
|
||||||
|
/// The currently displayed STNode
|
||||||
|
/// </summary>
|
||||||
|
[Description("Currently displayed STNode"), Browsable(false)]
|
||||||
|
public STNode STNode {
|
||||||
|
get { return _STNode; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _ItemHoverColor = Color.FromArgb(50, 125, 125, 125);
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the background color when the property option is hovered by the mouse
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color when the property option is hovered by the mouse")]
|
||||||
|
public Color ItemHoverColor {
|
||||||
|
get { return _ItemHoverColor; }
|
||||||
|
set { _ItemHoverColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _ItemSelectedColor = Color.DodgerBlue;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the background color when the property option is selected. This property cannot be set when AutoColor is set
|
||||||
|
/// </summary>
|
||||||
|
[Description("Background color when get or set property option is selected and this property cannot be set when AutoColor is set"), DefaultValue(typeof(Color), "DodgerBlue")]
|
||||||
|
public Color ItemSelectedColor {
|
||||||
|
get { return _ItemSelectedColor; }
|
||||||
|
set {
|
||||||
|
if (this._AutoColor) return;
|
||||||
|
if (value == _ItemSelectedColor) return;
|
||||||
|
_ItemSelectedColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _ItemValueBackColor = Color.FromArgb(255, 80, 80, 80);
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the background color of the attribute option value
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set attribute option value background color")]
|
||||||
|
public Color ItemValueBackColor {
|
||||||
|
get { return _ItemValueBackColor; }
|
||||||
|
set {
|
||||||
|
_ItemValueBackColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _TitleColor = Color.FromArgb(127, 0, 0, 0);
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default title background color
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the default title background color")]
|
||||||
|
public Color TitleColor {
|
||||||
|
get { return _TitleColor; }
|
||||||
|
set {
|
||||||
|
_TitleColor = value;
|
||||||
|
if (!this._ShowTitle) return;
|
||||||
|
this.Invalidate(m_rect_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _ErrorColor = Color.FromArgb(200, Color.Brown);
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the background color of the prompt message when the property is set incorrectly
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color of the message when the property is set incorrectly")]
|
||||||
|
public Color ErrorColor {
|
||||||
|
get { return _ErrorColor; }
|
||||||
|
set { _ErrorColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _DescriptionColor = Color.FromArgb(200, Color.DarkGoldenrod);
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the background color of the attribute description information
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color of attribute description information")]
|
||||||
|
public Color DescriptionColor {
|
||||||
|
get { return _DescriptionColor; }
|
||||||
|
set { _DescriptionColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _ShowTitle = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to display the node title
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether to display the node title")]
|
||||||
|
public bool ShowTitle {
|
||||||
|
get { return _ShowTitle; }
|
||||||
|
set {
|
||||||
|
_ShowTitle = value;
|
||||||
|
this.SetItemRectangle();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _AutoColor = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set whether to automatically set the control highlight color according to STNode
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether to automatically set the control highlight color according to STNode"), DefaultValue(true)]
|
||||||
|
public bool AutoColor {
|
||||||
|
get { return _AutoColor; }
|
||||||
|
set { _AutoColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _InfoFirstOnDraw;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or whether the information panel is drawn first when the node is set
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether to draw the information panel first when the node is set"), DefaultValue(false)]
|
||||||
|
public bool InfoFirstOnDraw {
|
||||||
|
get { return _InfoFirstOnDraw; }
|
||||||
|
set { _InfoFirstOnDraw = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _ReadOnlyModel;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the current property editor is in read-only mode
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether the current property editor is in read-only mode"), DefaultValue(false)]
|
||||||
|
public bool ReadOnlyModel {
|
||||||
|
get { return _ReadOnlyModel; }
|
||||||
|
set {
|
||||||
|
if (value == _ReadOnlyModel) return;
|
||||||
|
_ReadOnlyModel = value;
|
||||||
|
this.Invalidate(m_rect_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current scroll bar height
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get the current scroll bar height")]
|
||||||
|
public int ScrollOffset { get { return m_nOffsetY; } }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region protected fields ==========
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Author link address area
|
||||||
|
/// </summary>
|
||||||
|
protected Rectangle m_rect_link;
|
||||||
|
/// <summary>
|
||||||
|
/// View the help button area
|
||||||
|
/// </summary>
|
||||||
|
protected Rectangle m_rect_help;
|
||||||
|
/// <summary>
|
||||||
|
/// Editor title area
|
||||||
|
/// </summary>
|
||||||
|
protected Rectangle m_rect_title;
|
||||||
|
/// <summary>
|
||||||
|
/// Panel toggle button area
|
||||||
|
/// </summary>
|
||||||
|
protected Rectangle m_rect_switch;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The vertical scroll offset used by the control during drawing
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nOffsetY;
|
||||||
|
/// <summary>
|
||||||
|
/// Saved info panel vertical scroll offset
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nInfoOffsetY;
|
||||||
|
/// <summary>
|
||||||
|
/// Saved property panel vertical scroll offset
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nPropertyOffsetY;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total height of the drawing area used by the control during drawing
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nVHeight;
|
||||||
|
/// <summary>
|
||||||
|
/// The total height required for the saved info panel
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nInfoVHeight;
|
||||||
|
/// <summary>
|
||||||
|
/// The total height required by the saved property panel
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nPropertyVHeight;
|
||||||
|
/// <summary>
|
||||||
|
/// Key in the information panel displays the required horizontal width
|
||||||
|
/// </summary>
|
||||||
|
protected int m_nInfoLeft;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private Type m_type;
|
||||||
|
private string[] m_KeysString = new string[] { "author", "email", "link", "view help" };
|
||||||
|
|
||||||
|
private int m_nTitleHeight = 20;
|
||||||
|
private int m_item_height = 30;
|
||||||
|
private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);
|
||||||
|
private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);
|
||||||
|
//All property lists are saved in this List
|
||||||
|
private List<STNodePropertyDescriptor> m_lst_item = new List<STNodePropertyDescriptor>();
|
||||||
|
|
||||||
|
private STNodePropertyDescriptor m_item_hover; //The currently hovered option
|
||||||
|
private STNodePropertyDescriptor m_item_hover_value; //The current value area is hovered by the mouse
|
||||||
|
private STNodePropertyDescriptor m_item_down_value; //The option that the current value area is clicked by the mouse
|
||||||
|
private STNodePropertyDescriptor m_item_selected; //The currently selected option
|
||||||
|
private STNodeAttribute m_node_attribute; //Node parameter information
|
||||||
|
private bool m_b_hover_switch; //Whether the mouse hovers over the panel switch button
|
||||||
|
private bool m_b_current_draw_info; //The current drawing is the information panel
|
||||||
|
|
||||||
|
private Point m_pt_move; //The real-time coordinates of the mouse on the control
|
||||||
|
private Point m_pt_down; //The coordinates of the last mouse click on the control
|
||||||
|
private string m_str_err; // draw error message when set
|
||||||
|
private string m_str_desc; //Draw description information when set
|
||||||
|
|
||||||
|
private Pen m_pen;
|
||||||
|
private SolidBrush m_brush;
|
||||||
|
private StringFormat m_sf;
|
||||||
|
private DrawingTools m_dt;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a node attribute editor
|
||||||
|
/// </summary>
|
||||||
|
public STNodePropertyGrid() {
|
||||||
|
this.SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||||
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||||
|
|
||||||
|
m_pen = new Pen(Color.Black, 1);
|
||||||
|
m_brush = new SolidBrush(Color.Black);
|
||||||
|
m_sf = new StringFormat();
|
||||||
|
m_sf.LineAlignment = StringAlignment.Center;
|
||||||
|
m_sf.FormatFlags = StringFormatFlags.NoWrap;
|
||||||
|
m_dt.Pen = m_pen;
|
||||||
|
m_dt.SolidBrush = m_brush;
|
||||||
|
|
||||||
|
this.ForeColor = Color.White;
|
||||||
|
this.BackColor = Color.FromArgb(255, 35, 35, 35);
|
||||||
|
|
||||||
|
this.MinimumSize = new Size(120, 50);
|
||||||
|
this.Size = new Size(200, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private method ==========
|
||||||
|
|
||||||
|
private List<STNodePropertyDescriptor> GetProperties(STNode node) {
|
||||||
|
List<STNodePropertyDescriptor> lst = new List<STNodePropertyDescriptor>();
|
||||||
|
if (node == null) return lst;
|
||||||
|
Type t = node.GetType();
|
||||||
|
foreach (var p in t.GetProperties()) {
|
||||||
|
var attrs = p.GetCustomAttributes(true);
|
||||||
|
foreach (var a in attrs) {
|
||||||
|
if (!(a is STNodePropertyAttribute)) continue;
|
||||||
|
var attr = a as STNodePropertyAttribute;
|
||||||
|
object obj = Activator.CreateInstance(attr.DescriptorType);
|
||||||
|
if (!(obj is STNodePropertyDescriptor))
|
||||||
|
throw new ArgumentException("[STNodePropertyAttribute.DescriptorType] parameter value must be the type of [STNodePropertyDescriptor] or its subclass");
|
||||||
|
var desc = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
|
||||||
|
desc.Node = node;
|
||||||
|
desc.Name = attr.Name;
|
||||||
|
desc.Description = attr.Description;
|
||||||
|
desc.PropertyInfo = p;
|
||||||
|
desc.Control = this;
|
||||||
|
lst.Add(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
private STNodeAttribute GetNodeAttribute(STNode node) {
|
||||||
|
if (node == null) return null;
|
||||||
|
Type t = node.GetType();
|
||||||
|
foreach (var v in t.GetCustomAttributes(true)) {
|
||||||
|
if (!(v is STNodeAttribute)) continue;
|
||||||
|
return (STNodeAttribute)v;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetItemRectangle() {
|
||||||
|
int nw_p = 0, nw_h = 0;
|
||||||
|
using (Graphics g = this.CreateGraphics()) {
|
||||||
|
foreach (var v in m_lst_item) {
|
||||||
|
SizeF szf = g.MeasureString(v.Name, this.Font);
|
||||||
|
if (szf.Width > nw_p) nw_p = (int)Math.Ceiling(szf.Width);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < m_KeysString.Length - 1; i++) {
|
||||||
|
SizeF szf = g.MeasureString(m_KeysString[i], this.Font);
|
||||||
|
if (szf.Width > nw_h) nw_h = (int)Math.Ceiling(szf.Width);
|
||||||
|
}
|
||||||
|
nw_p += 5; nw_h += 5;
|
||||||
|
nw_p = Math.Min(nw_p, this.Width >> 1);
|
||||||
|
m_nInfoLeft = Math.Min(nw_h, this.Width >> 1);
|
||||||
|
|
||||||
|
int nTitleHeight = this._ShowTitle ? m_nTitleHeight : 0;
|
||||||
|
for (int i = 0; i < m_lst_item.Count; i++) {
|
||||||
|
STNodePropertyDescriptor item = m_lst_item[i];
|
||||||
|
Rectangle rect = new Rectangle(0, i * m_item_height + nTitleHeight, this.Width, m_item_height);
|
||||||
|
item.Rectangle = rect;
|
||||||
|
rect.Width = nw_p;
|
||||||
|
item.RectangleL = rect;
|
||||||
|
rect.X = rect.Right;
|
||||||
|
rect.Width = this.Width - rect.Left - 1;
|
||||||
|
rect.Inflate(-4, -4);
|
||||||
|
item.RectangleR = rect;
|
||||||
|
item.OnSetItemLocation();
|
||||||
|
}
|
||||||
|
m_nPropertyVHeight = m_lst_item.Count * m_item_height;
|
||||||
|
if (this._ShowTitle) m_nPropertyVHeight += m_nTitleHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region override ==========
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the control is redrawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnPaint(PaintEventArgs e) {
|
||||||
|
base.OnPaint(e);
|
||||||
|
Graphics g = e.Graphics;
|
||||||
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
|
||||||
|
m_dt.Graphics = g;
|
||||||
|
|
||||||
|
m_nOffsetY = m_b_current_draw_info ? m_nInfoOffsetY : m_nPropertyOffsetY;
|
||||||
|
g.TranslateTransform(0, m_nOffsetY);
|
||||||
|
|
||||||
|
if (m_b_current_draw_info) {
|
||||||
|
m_nVHeight = m_nInfoVHeight;
|
||||||
|
this.OnDrawInfo(m_dt);
|
||||||
|
} else {
|
||||||
|
m_nVHeight = m_nPropertyVHeight;
|
||||||
|
for (int i = 0; i < m_lst_item.Count; i++) {
|
||||||
|
this.OnDrawPropertyItem(m_dt, m_lst_item[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.ResetTransform();
|
||||||
|
|
||||||
|
if (this._ShowTitle) this.OnDrawTitle(m_dt);
|
||||||
|
m_sf.FormatFlags = 0;
|
||||||
|
if (!string.IsNullOrEmpty(m_str_err)) this.OnDrawErrorInfo(m_dt);
|
||||||
|
if (!string.IsNullOrEmpty(m_str_desc)) this.OnDrawDescription(m_dt);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse moves over the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnMouseMove(MouseEventArgs e) {
|
||||||
|
base.OnMouseMove(e);
|
||||||
|
m_pt_move = e.Location;
|
||||||
|
bool bHover = this._ShowTitle && m_rect_switch.Contains(e.Location);
|
||||||
|
if (bHover != m_b_hover_switch) {
|
||||||
|
m_b_hover_switch = bHover;
|
||||||
|
this.Invalidate(m_rect_switch);
|
||||||
|
}
|
||||||
|
Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
|
||||||
|
MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
|
||||||
|
if (m_b_current_draw_info)
|
||||||
|
this.OnProcessHelpMouseMove(mea);
|
||||||
|
else
|
||||||
|
this.OnProcessPropertyMouseMove(mea);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is clicked on the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnMouseDown(MouseEventArgs e) {
|
||||||
|
base.OnMouseDown(e);
|
||||||
|
m_pt_down = e.Location;
|
||||||
|
this.Focus();
|
||||||
|
bool bRedraw = false;
|
||||||
|
if (m_str_err != null) {
|
||||||
|
bRedraw = true;
|
||||||
|
m_str_err = null;
|
||||||
|
}
|
||||||
|
if (this._ShowTitle) {
|
||||||
|
if (m_rect_switch.Contains(e.Location)) {
|
||||||
|
if (m_node_attribute == null || m_lst_item.Count == 0) return;
|
||||||
|
m_b_current_draw_info = !m_b_current_draw_info;
|
||||||
|
this.Invalidate();
|
||||||
|
return;
|
||||||
|
} else if (m_rect_title.Contains(e.Location)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this._ShowTitle && m_rect_switch.Contains(e.Location)) {
|
||||||
|
if (m_node_attribute == null || m_lst_item.Count == 0) return;
|
||||||
|
m_b_current_draw_info = !m_b_current_draw_info;
|
||||||
|
this.Invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
|
||||||
|
MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
|
||||||
|
|
||||||
|
if (m_b_current_draw_info)
|
||||||
|
this.OnProcessInfoMouseDown(mea);
|
||||||
|
else
|
||||||
|
this.OnProcessPropertyMouseDown(mea);
|
||||||
|
if (bRedraw) this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is raised over the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnMouseUp(MouseEventArgs e) {
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
m_str_desc = null;
|
||||||
|
if (m_item_down_value != null && !this._ReadOnlyModel) {
|
||||||
|
Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
|
||||||
|
MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
|
||||||
|
m_item_down_value.OnMouseUp(mea);
|
||||||
|
if (m_pt_down == e.Location && !this._ReadOnlyModel) {
|
||||||
|
m_item_down_value.OnMouseClick(mea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_item_down_value = null;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse leaves the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnMouseLeave(EventArgs e) {
|
||||||
|
base.OnMouseLeave(e);
|
||||||
|
m_b_hover_switch = false;
|
||||||
|
if (m_item_hover_value != null && !this._ReadOnlyModel) m_item_hover_value.OnMouseLeave(e);
|
||||||
|
m_item_hover = null;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse rolls the wheel on the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnMouseWheel(MouseEventArgs e) {
|
||||||
|
base.OnMouseWheel(e);
|
||||||
|
if (e.Delta > 0) {
|
||||||
|
if (m_nOffsetY == 0) return;
|
||||||
|
m_nOffsetY += m_item_height;
|
||||||
|
if (m_nOffsetY > 0) m_nOffsetY = 0;
|
||||||
|
} else {
|
||||||
|
if (this.Height - m_nOffsetY >= m_nVHeight) return;
|
||||||
|
m_nOffsetY -= m_item_height;
|
||||||
|
}
|
||||||
|
if (m_b_current_draw_info)
|
||||||
|
m_nInfoOffsetY = m_nOffsetY;
|
||||||
|
else
|
||||||
|
m_nPropertyOffsetY = m_nOffsetY;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the control rectangle area is set
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">x coordinate</param>
|
||||||
|
/// <param name="y">y coordinate</param>
|
||||||
|
/// <param name="width">width</param>
|
||||||
|
/// <param name="height">height</param>
|
||||||
|
/// <param name="specified">Specify the identifier to be set</param>
|
||||||
|
//protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
|
||||||
|
// if (width < 120) width = 120;
|
||||||
|
// if (height < 50) height = 50;
|
||||||
|
// base.SetBoundsCore(x, y, width, height, specified);
|
||||||
|
//}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the control size changes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Event parameters</param>
|
||||||
|
protected override void OnResize(EventArgs e) {
|
||||||
|
base.OnResize(e);
|
||||||
|
m_rect_title.Width = this.Width;
|
||||||
|
m_rect_title.Height = m_nTitleHeight;
|
||||||
|
if (this._ShowTitle)
|
||||||
|
m_rect_switch = new Rectangle(this.Width - m_nTitleHeight + 2, 2, m_nTitleHeight - 4, m_nTitleHeight - 4);
|
||||||
|
if (this._STNode != null) this.SetItemRectangle();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region virtual method ==========
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when drawing attribute options
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
/// <param name="item">Target attribute option descriptor</param>
|
||||||
|
/// <param name="nIndex">The index of the option</param>
|
||||||
|
protected virtual void OnDrawPropertyItem(DrawingTools dt, STNodePropertyDescriptor item, int nIndex) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
m_brush.Color = (nIndex % 2 == 0) ? m_clr_item_1 : m_clr_item_2;
|
||||||
|
g.FillRectangle(m_brush, item.Rectangle);
|
||||||
|
if (item == m_item_hover || item == m_item_selected) {
|
||||||
|
m_brush.Color = this._ItemHoverColor;
|
||||||
|
g.FillRectangle(m_brush, item.Rectangle);
|
||||||
|
}
|
||||||
|
if (m_item_selected == item) {
|
||||||
|
g.FillRectangle(m_brush, item.Rectangle.X, item.Rectangle.Y, 5, item.Rectangle.Height);
|
||||||
|
if (this._AutoColor && this._STNode != null)
|
||||||
|
m_brush.Color = this._STNode.TitleColor;
|
||||||
|
else
|
||||||
|
m_brush.Color = this._ItemSelectedColor;
|
||||||
|
g.FillRectangle(m_brush, item.Rectangle.X, item.Rectangle.Y + 4, 5, item.Rectangle.Height - 8);
|
||||||
|
}
|
||||||
|
m_sf.Alignment = StringAlignment.Far;
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
g.DrawString(item.Name, this.Font, m_brush, item.RectangleL, m_sf);
|
||||||
|
|
||||||
|
item.OnDrawValueRectangle(m_dt);
|
||||||
|
if (this._ReadOnlyModel) {
|
||||||
|
m_brush.Color = Color.FromArgb(125, 125, 125, 125);
|
||||||
|
g.FillRectangle(m_brush, item.RectangleR);
|
||||||
|
m_pen.Color = this.ForeColor;
|
||||||
|
//g.DrawLine(m_pen,
|
||||||
|
// item.RectangleR.Left - 2, item.RectangleR.Top + item.RectangleR.Height / 2,
|
||||||
|
// item.RectangleR.Right + 1, item.RectangleR.Top + item.RectangleR.Height / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Draw the property window title
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
protected virtual void OnDrawTitle(DrawingTools dt) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
if (this._AutoColor)
|
||||||
|
m_brush.Color = this._STNode == null ? this._TitleColor : this._STNode.TitleColor;
|
||||||
|
else
|
||||||
|
m_brush.Color = this._TitleColor;
|
||||||
|
g.FillRectangle(m_brush, m_rect_title);
|
||||||
|
m_brush.Color = this._STNode == null ? this.ForeColor : this._STNode.ForeColor;
|
||||||
|
m_sf.Alignment = StringAlignment.Center;
|
||||||
|
g.DrawString(this._STNode == null ? this.Text : this._STNode.Title, this.Font, m_brush, m_rect_title, m_sf);
|
||||||
|
|
||||||
|
if (this._ReadOnlyModel) {
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
g.FillRectangle(dt.SolidBrush, 4, 5, 2, 4);
|
||||||
|
g.FillRectangle(dt.SolidBrush, 6, 5, 2, 2);
|
||||||
|
g.FillRectangle(dt.SolidBrush, 8, 5, 2, 4);
|
||||||
|
g.FillRectangle(dt.SolidBrush, 3, 9, 8, 6);
|
||||||
|
}
|
||||||
|
//Whether to draw the panel switch button
|
||||||
|
if (m_node_attribute == null || m_lst_item.Count == 0) return;
|
||||||
|
if (m_b_hover_switch) {
|
||||||
|
m_brush.Color = this.BackColor;
|
||||||
|
g.FillRectangle(m_brush, m_rect_switch);
|
||||||
|
}
|
||||||
|
m_pen.Color = this._STNode == null ? this.ForeColor : this._STNode.ForeColor;
|
||||||
|
m_brush.Color = m_pen.Color;
|
||||||
|
int nT1 = m_rect_switch.Top + m_rect_switch.Height / 2 - 2;
|
||||||
|
int nT2 = m_rect_switch.Top + m_rect_switch.Height / 2 + 1;
|
||||||
|
g.DrawRectangle(m_pen, m_rect_switch.Left, m_rect_switch.Top, m_rect_switch.Width - 1, m_rect_switch.Height - 1);
|
||||||
|
|
||||||
|
g.DrawLines(m_pen, new Point[]{
|
||||||
|
new Point(m_rect_switch.Left + 2, nT1), new Point(m_rect_switch.Right - 3, nT1),
|
||||||
|
new Point(m_rect_switch.Left + 3, nT1 - 1), new Point(m_rect_switch.Right - 3, nT1 - 1)
|
||||||
|
});
|
||||||
|
g.DrawLines(m_pen, new Point[]{
|
||||||
|
new Point(m_rect_switch.Left + 2, nT2), new Point(m_rect_switch.Right - 3, nT2),
|
||||||
|
new Point(m_rect_switch.Left + 2, nT2 + 1), new Point(m_rect_switch.Right - 4, nT2 + 1),
|
||||||
|
});
|
||||||
|
|
||||||
|
g.FillPolygon(m_brush, new Point[]{
|
||||||
|
new Point(m_rect_switch.Left + 2, nT1),
|
||||||
|
new Point(m_rect_switch.Left + 7, nT1),
|
||||||
|
new Point(m_rect_switch.Left + 7, m_rect_switch.Top ),
|
||||||
|
});
|
||||||
|
g.FillPolygon(m_brush, new Point[]{
|
||||||
|
new Point(m_rect_switch.Right - 2, nT2),
|
||||||
|
new Point(m_rect_switch.Right - 7, nT2),
|
||||||
|
new Point(m_rect_switch.Right - 7, m_rect_switch.Bottom - 2 ),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the attribute description information needs to be drawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
protected virtual void OnDrawDescription(DrawingTools dt) {
|
||||||
|
if (string.IsNullOrEmpty(m_str_desc)) return;
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
SizeF szf = g.MeasureString(m_str_desc, this.Font, this.Width - 4);
|
||||||
|
Rectangle rect_desc = new Rectangle(0, this.Height - (int)szf.Height - 4, this.Width, (int)szf.Height + 4);
|
||||||
|
m_brush.Color = this._DescriptionColor;
|
||||||
|
g.FillRectangle(m_brush, rect_desc);
|
||||||
|
m_pen.Color = this._DescriptionColor;
|
||||||
|
g.DrawRectangle(m_pen, 0, rect_desc.Top, rect_desc.Width - 1, rect_desc.Height - 1);
|
||||||
|
rect_desc.Inflate(-4, 0);
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
m_sf.Alignment = StringAlignment.Near;
|
||||||
|
g.DrawString(m_str_desc, this.Font, m_brush, rect_desc, m_sf);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when an error message needs to be drawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
protected virtual void OnDrawErrorInfo(DrawingTools dt) {
|
||||||
|
if (string.IsNullOrEmpty(m_str_err)) return;
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
SizeF szf = g.MeasureString(m_str_err, this.Font, this.Width - 4);
|
||||||
|
Rectangle rect_desc = new Rectangle(0, 0, this.Width, (int)szf.Height + 4);
|
||||||
|
m_brush.Color = this._ErrorColor;
|
||||||
|
g.FillRectangle(m_brush, rect_desc);
|
||||||
|
m_pen.Color = this._ErrorColor;
|
||||||
|
g.DrawRectangle(m_pen, 0, rect_desc.Top, rect_desc.Width - 1, rect_desc.Height - 1);
|
||||||
|
rect_desc.Inflate(-4, 0);
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
m_sf.Alignment = StringAlignment.Near;
|
||||||
|
g.DrawString(m_str_err, this.Font, m_brush, rect_desc, m_sf);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when drawing node information
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
protected virtual void OnDrawInfo(DrawingTools dt) {
|
||||||
|
if (m_node_attribute == null) return;
|
||||||
|
var attr = m_node_attribute;
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
Color clr_r = Color.FromArgb(this.ForeColor.A / 2, this.ForeColor);
|
||||||
|
m_sf.Alignment = StringAlignment.Near;
|
||||||
|
Rectangle rect = new Rectangle(0, this._ShowTitle ? m_nTitleHeight : 0, this.Width, m_item_height);
|
||||||
|
Rectangle rect_l = new Rectangle(2, rect.Top, m_nInfoLeft - 2, m_item_height);
|
||||||
|
Rectangle rect_r = new Rectangle(m_nInfoLeft, rect.Top, this.Width - m_nInfoLeft, m_item_height);
|
||||||
|
m_brush.Color = m_clr_item_2;
|
||||||
|
g.FillRectangle(m_brush, rect);
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
m_sf.FormatFlags = StringFormatFlags.NoWrap;
|
||||||
|
m_sf.Alignment = StringAlignment.Near;
|
||||||
|
g.DrawString(m_KeysString[0], this.Font, m_brush, rect_l, m_sf); //author
|
||||||
|
m_brush.Color = clr_r;
|
||||||
|
g.DrawString(attr.Author, this.Font, m_brush, rect_r, m_sf);
|
||||||
|
rect.Y += m_item_height; rect_l.Y += m_item_height; rect_r.Y += m_item_height;
|
||||||
|
|
||||||
|
m_brush.Color = m_clr_item_1;
|
||||||
|
g.FillRectangle(m_brush, rect);
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
g.DrawString(m_KeysString[1], this.Font, m_brush, rect_l, m_sf); //mail
|
||||||
|
m_brush.Color = clr_r;
|
||||||
|
g.DrawString(attr.Mail, this.Font, m_brush, rect_r, m_sf);
|
||||||
|
rect.Y += m_item_height; rect_l.Y += m_item_height; rect_r.Y += m_item_height;
|
||||||
|
|
||||||
|
m_brush.Color = m_clr_item_2;
|
||||||
|
g.FillRectangle(m_brush, rect);
|
||||||
|
m_brush.Color = this.ForeColor;
|
||||||
|
g.DrawString(m_KeysString[2], this.Font, m_brush, rect_l, m_sf); //link_key
|
||||||
|
m_brush.Color = clr_r;
|
||||||
|
g.DrawString(attr.Link, this.Font, Brushes.CornflowerBlue, rect_r, m_sf); //link
|
||||||
|
if (!string.IsNullOrEmpty(attr.Link)) m_rect_link = rect_r;
|
||||||
|
//fill left
|
||||||
|
m_brush.Color = Color.FromArgb(40, 125, 125, 125);
|
||||||
|
g.FillRectangle(m_brush, 0, this._ShowTitle ? m_nTitleHeight : 0, m_nInfoLeft - 1, m_item_height * 3);
|
||||||
|
|
||||||
|
rect.X = 5; rect.Y += m_item_height;
|
||||||
|
rect.Width = this.Width - 10;
|
||||||
|
if (!string.IsNullOrEmpty(m_node_attribute.Description)) {
|
||||||
|
float h = g.MeasureString(m_node_attribute.Description, this.Font, rect.Width).Height;
|
||||||
|
rect.Height = (int)Math.Ceiling(h / m_item_height) * m_item_height;
|
||||||
|
m_brush.Color = clr_r;
|
||||||
|
m_sf.FormatFlags = 0;
|
||||||
|
g.DrawString(m_node_attribute.Description, this.Font, m_brush, rect, m_sf);
|
||||||
|
}
|
||||||
|
m_nInfoVHeight = rect.Bottom;
|
||||||
|
bool bHasHelp = STNodeAttribute.GetHelpMethod(m_type) != null;
|
||||||
|
rect.X = 5; rect.Y += rect.Height;
|
||||||
|
rect.Height = m_item_height;
|
||||||
|
m_sf.Alignment = StringAlignment.Center;
|
||||||
|
m_brush.Color = Color.FromArgb(125, 125, 125, 125);
|
||||||
|
g.FillRectangle(m_brush, rect);
|
||||||
|
if (bHasHelp) m_brush.Color = Color.CornflowerBlue;
|
||||||
|
g.DrawString(m_KeysString[3], this.Font, m_brush, rect, m_sf);
|
||||||
|
if (bHasHelp) m_rect_help = rect;
|
||||||
|
else {
|
||||||
|
int w = (int)g.MeasureString(m_KeysString[3], this.Font).Width + 1;
|
||||||
|
int x = rect.X + (rect.Width - w) / 2, y = rect.Y + rect.Height / 2;
|
||||||
|
m_pen.Color = m_brush.Color;
|
||||||
|
g.DrawLine(m_pen, x, y, x + w, y);
|
||||||
|
}
|
||||||
|
m_nInfoVHeight = rect.Bottom;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is clicked on the property panel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Mouse event parameters</param>
|
||||||
|
protected virtual void OnProcessPropertyMouseDown(MouseEventArgs e) {
|
||||||
|
bool bRedraw = false;
|
||||||
|
if (m_item_selected != m_item_hover) {
|
||||||
|
m_item_selected = m_item_hover;
|
||||||
|
bRedraw = true;
|
||||||
|
}
|
||||||
|
m_item_down_value = null;
|
||||||
|
if (m_item_selected == null) {
|
||||||
|
if (bRedraw) this.Invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_item_selected.RectangleR.Contains(e.Location)) {
|
||||||
|
m_item_down_value = m_item_selected;
|
||||||
|
if (!this._ReadOnlyModel)
|
||||||
|
m_item_selected.OnMouseDown(e);
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (m_item_selected.RectangleL.Contains(e.Location)) {
|
||||||
|
m_str_desc = m_item_selected.Description;
|
||||||
|
bRedraw = true;
|
||||||
|
}
|
||||||
|
if (bRedraw) this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is clicked in the info panel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Mouse event parameters</param>
|
||||||
|
protected virtual void OnProcessInfoMouseDown(MouseEventArgs e) {
|
||||||
|
try {
|
||||||
|
if (m_rect_link.Contains(e.Location)) {
|
||||||
|
System.Diagnostics.Process.Start(m_node_attribute.Link);
|
||||||
|
} else if (m_rect_help.Contains(e.Location)) {
|
||||||
|
STNodeAttribute.ShowHelp(m_type);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
this.SetErrorMessage(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is moved in the properties panel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Mouse event parameters</param>
|
||||||
|
protected virtual void OnProcessPropertyMouseMove(MouseEventArgs e) {
|
||||||
|
if (m_item_down_value != null) {
|
||||||
|
m_item_down_value.OnMouseMove(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
STNodePropertyDescriptor item = null;
|
||||||
|
foreach (var v in m_lst_item) {
|
||||||
|
if (v.Rectangle.Contains(e.Location)) {
|
||||||
|
item = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item != null) {
|
||||||
|
if (item.RectangleR.Contains(e.Location)) {
|
||||||
|
if (m_item_hover_value != item) {
|
||||||
|
if (m_item_hover_value != null) m_item_hover_value.OnMouseLeave(e);
|
||||||
|
m_item_hover_value = item;
|
||||||
|
m_item_hover_value.OnMouseEnter(e);
|
||||||
|
}
|
||||||
|
m_item_hover_value.OnMouseMove(e);
|
||||||
|
} else {
|
||||||
|
if (m_item_hover_value != null) m_item_hover_value.OnMouseLeave(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_item_hover != item) {
|
||||||
|
m_item_hover = item;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the mouse is moved in the info panel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Mouse event parameters</param>
|
||||||
|
protected virtual void OnProcessHelpMouseMove(MouseEventArgs e) {
|
||||||
|
if (m_rect_link.Contains(e.Location) || m_rect_help.Contains(e.Location)) {
|
||||||
|
this.Cursor = Cursors.Hand;
|
||||||
|
} else this.Cursor = Cursors.Arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region public ==========
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the STNode node that needs to be displayed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">target node</param>
|
||||||
|
public void SetNode(STNode node) {
|
||||||
|
if (node == this._STNode) return;
|
||||||
|
m_nInfoOffsetY = m_nPropertyOffsetY = 0;
|
||||||
|
m_nInfoVHeight = m_nPropertyVHeight = 0;
|
||||||
|
m_rect_link = m_rect_help = Rectangle.Empty;
|
||||||
|
m_str_desc = m_str_err = null;
|
||||||
|
this._STNode = node;
|
||||||
|
if (node != null) {
|
||||||
|
m_type = node.GetType();
|
||||||
|
m_lst_item = this.GetProperties(node);
|
||||||
|
m_node_attribute = this.GetNodeAttribute(node);
|
||||||
|
this.SetItemRectangle();
|
||||||
|
m_b_current_draw_info = m_lst_item.Count == 0 || this._InfoFirstOnDraw;
|
||||||
|
if (this._AutoColor) this._ItemSelectedColor = this._STNode.TitleColor;
|
||||||
|
} else {
|
||||||
|
m_type = null;
|
||||||
|
m_lst_item.Clear();
|
||||||
|
m_node_attribute = null;
|
||||||
|
}
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Set the display text of the Key of the information page
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strAuthor">author</param>
|
||||||
|
/// <param name="strMail">mail</param>
|
||||||
|
/// <param name="strLink">connect</param>
|
||||||
|
/// <param name="strHelp">View help</param>
|
||||||
|
public void SetInfoKey(string strAuthor, string strMail, string strLink, string strHelp) {
|
||||||
|
m_KeysString = new string[] { strAuthor, strMail, strLink, strHelp };
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Set the error message to display
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strText">Error message</param>
|
||||||
|
public void SetErrorMessage(string strText) {
|
||||||
|
m_str_err = strText;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
907
CodeWalker.WinForms/STNodeEditor/STNodeTreeView.cs
Normal file
907
CodeWalker.WinForms/STNodeEditor/STNodeTreeView.cs
Normal file
@ -0,0 +1,907 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Collections;
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 DebugST@crystal_lz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* create: 2021-02-23
|
||||||
|
* modify: 2021-03-02
|
||||||
|
* Author: Crystal_lz
|
||||||
|
* blog: http://st233.com
|
||||||
|
* Gitee: https://gitee.com/DebugST
|
||||||
|
* Github: https://github.com/DebugST
|
||||||
|
*/
|
||||||
|
namespace ST.Library.UI.NodeEditor
|
||||||
|
{
|
||||||
|
public class STNodeTreeView : Control
|
||||||
|
{
|
||||||
|
private Color _ItemBackColor = Color.FromArgb(255, 45, 45, 45);
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the background color of each row attribute option
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color of each row attribute option")]
|
||||||
|
public Color ItemBackColor {
|
||||||
|
get { return _ItemBackColor; }
|
||||||
|
set {
|
||||||
|
_ItemBackColor = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _ItemHoverColor = Color.FromArgb(50, 125, 125, 125);
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the background color when the property option is hovered by the mouse
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color when the property option is hovered by the mouse")]
|
||||||
|
public Color ItemHoverColor {
|
||||||
|
get { return _ItemHoverColor; }
|
||||||
|
set { _ItemHoverColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _TitleColor = Color.FromArgb(255, 60, 60, 60);
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the background color of the top retrieval area
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color of the top retrieval area")]
|
||||||
|
public Color TitleColor {
|
||||||
|
get { return _TitleColor; }
|
||||||
|
set {
|
||||||
|
_TitleColor = value;
|
||||||
|
this.Invalidate(new Rectangle(0, 0, this.Width, m_nItemHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the background color of the search text box
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the background color of the search text box")]
|
||||||
|
public Color TextBoxColor {
|
||||||
|
get { return m_tbx.BackColor; }
|
||||||
|
set {
|
||||||
|
m_tbx.BackColor = value;
|
||||||
|
this.Invalidate(new Rectangle(0, 0, this.Width, m_nItemHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _HightLightTextColor = Color.Lime;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the highlighted text color when retrieving
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the highlighted text color during retrieval"), DefaultValue(typeof(Color), "Lime")]
|
||||||
|
public Color HightLightTextColor {
|
||||||
|
get { return _HightLightTextColor; }
|
||||||
|
set { _HightLightTextColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _InfoButtonColor = Color.Gray;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color of the information display button. If AutoColor is set, this property value cannot be set
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the color of the information display button. If AutoColor is set, this property value cannot be set"), DefaultValue(typeof(Color), "Gray")]
|
||||||
|
public Color InfoButtonColor {
|
||||||
|
get { return _InfoButtonColor; }
|
||||||
|
set { _InfoButtonColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _FolderCountColor = Color.FromArgb(40, 255, 255, 255);
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the text color of the count
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the text color of statistics")]
|
||||||
|
public Color FolderCountColor {
|
||||||
|
get { return _FolderCountColor; }
|
||||||
|
set { _FolderCountColor = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color _SwitchColor = Color.LightGray;
|
||||||
|
|
||||||
|
private bool _ShowFolderCount = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set whether to count the number of STNodes
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether to count the number of STNodes"), DefaultValue(typeof(Color), "LightGray")]
|
||||||
|
public bool ShowFolderCount {
|
||||||
|
get { return _ShowFolderCount; }
|
||||||
|
set { _ShowFolderCount = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _ShowInfoButton = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to display the information button
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether to display the information button"), DefaultValue(true)]
|
||||||
|
public bool ShowInfoButton {
|
||||||
|
get { return _ShowInfoButton; }
|
||||||
|
set { _ShowInfoButton = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _InfoPanelIsLeftLayout = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the preview window is laid out to the left
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set whether the preview window is a left layout"), DefaultValue(true)]
|
||||||
|
public bool InfoPanelIsLeftLayout {
|
||||||
|
get { return _InfoPanelIsLeftLayout; }
|
||||||
|
set { _InfoPanelIsLeftLayout = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _AutoColor = true;
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the title color of the STNode corresponding to some colors in the control
|
||||||
|
/// </summary>
|
||||||
|
[Description("Get or set the title color of STNode corresponding to some colors in the control"), DefaultValue(true)]
|
||||||
|
public bool AutoColor {
|
||||||
|
get { return _AutoColor; }
|
||||||
|
set {
|
||||||
|
_AutoColor = value;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private STNodeEditor _Editor;
|
||||||
|
/// <summary>
|
||||||
|
/// STNodeEditor used to get the node preview
|
||||||
|
/// </summary>
|
||||||
|
[Description("STNodeEditor used when getting node preview"), Browsable(false)]
|
||||||
|
public STNodeEditor Editor {
|
||||||
|
get { return _Editor; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private STNodePropertyGrid _PropertyGrid;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the STNodePropertyGrid used when getting the node preview
|
||||||
|
/// </summary>
|
||||||
|
[Description("STNodePropertyGrid used when getting node preview"), Browsable(false)]
|
||||||
|
public STNodePropertyGrid PropertyGrid {
|
||||||
|
get { return _PropertyGrid; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int m_nItemHeight = 29;
|
||||||
|
|
||||||
|
private static Type m_type_node_base = typeof(STNode);
|
||||||
|
private static char[] m_chr_splitter = new char[] { '/', '\\' };
|
||||||
|
private STNodeTreeCollection m_items_draw;
|
||||||
|
private STNodeTreeCollection m_items_source = new STNodeTreeCollection("ROOT");
|
||||||
|
private Dictionary<Type, string> m_dic_all_type = new Dictionary<Type, string>();
|
||||||
|
|
||||||
|
private Pen m_pen;
|
||||||
|
private SolidBrush m_brush;
|
||||||
|
private StringFormat m_sf;
|
||||||
|
private DrawingTools m_dt;
|
||||||
|
private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);// Color.FromArgb(255, 40, 40, 40);
|
||||||
|
private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);// Color.FromArgb(255, 50, 50, 50);
|
||||||
|
|
||||||
|
private int m_nOffsetY; //The vertical height that needs to be offset when the control is drawn
|
||||||
|
private int m_nSourceOffsetY; //The vertical height that needs to be offset when drawing source data
|
||||||
|
private int m_nSearchOffsetY; //The vertical height that needs to be offset when drawing and retrieving data
|
||||||
|
private int m_nVHeight; //The total height required by the content in the control
|
||||||
|
|
||||||
|
private bool m_bHoverInfo; //Whether the mouse is currently hovering over the information display button
|
||||||
|
private STNodeTreeCollection m_item_hover; //The tree node currently hovered by the mouse
|
||||||
|
private Point m_pt_control; //The coordinates of the mouse on the control
|
||||||
|
private Point m_pt_offsety; //The coordinates of the mouse after the hammer is offset on the control
|
||||||
|
private Rectangle m_rect_clear; //Clear the search button area
|
||||||
|
|
||||||
|
private string m_str_search; // retrieved text
|
||||||
|
private TextBox m_tbx = new TextBox(); //Retrieve the textbox
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a STNode tree control
|
||||||
|
/// </summary>
|
||||||
|
public STNodeTreeView() {
|
||||||
|
this.SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||||
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||||
|
|
||||||
|
this.MinimumSize = new System.Drawing.Size(100, 60);
|
||||||
|
this.Size = new System.Drawing.Size(200, 150);
|
||||||
|
m_items_draw = m_items_source;
|
||||||
|
m_pen = new Pen(Color.Black);
|
||||||
|
m_brush = new SolidBrush(Color.White);
|
||||||
|
m_sf = new StringFormat();
|
||||||
|
m_sf.LineAlignment = StringAlignment.Center;
|
||||||
|
m_dt.Pen = m_pen;
|
||||||
|
m_dt.SolidBrush = m_brush;
|
||||||
|
this.ForeColor = Color.FromArgb(255, 220, 220, 220);
|
||||||
|
this.BackColor = Color.FromArgb(255, 35, 35, 35);
|
||||||
|
m_tbx.Left = 6;
|
||||||
|
m_tbx.BackColor = Color.FromArgb(255, 30, 30, 30);
|
||||||
|
m_tbx.ForeColor = this.ForeColor;
|
||||||
|
m_tbx.BorderStyle = BorderStyle.None;
|
||||||
|
m_tbx.MaxLength = 20;
|
||||||
|
m_tbx.TextChanged += new EventHandler(m_tbx_TextChanged);
|
||||||
|
this.Controls.Add(m_tbx);
|
||||||
|
this.AllowDrop = true;
|
||||||
|
|
||||||
|
this._Editor = new STNodeEditor();
|
||||||
|
this._PropertyGrid = new STNodePropertyGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private method ==========
|
||||||
|
|
||||||
|
private void m_tbx_TextChanged(object sender, EventArgs e) {
|
||||||
|
m_str_search = m_tbx.Text.Trim().ToLower();
|
||||||
|
m_nSearchOffsetY = 0;
|
||||||
|
if (m_str_search == string.Empty) {
|
||||||
|
m_items_draw = m_items_source;
|
||||||
|
this.Invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_items_draw = m_items_source.Copy();
|
||||||
|
this.Search(m_items_draw, new Stack<string>(), m_str_search);
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Search(STNodeTreeCollection items, Stack<string> stack, string strText) {
|
||||||
|
bool bFound = false;
|
||||||
|
string[] strName = new string[items.Count];
|
||||||
|
int nCounter = 0;
|
||||||
|
foreach (STNodeTreeCollection v in items) {
|
||||||
|
if (v.NameLower.IndexOf(strText) != -1) {
|
||||||
|
v.IsOpen = bFound = true;
|
||||||
|
} else {
|
||||||
|
if (!this.Search(v, stack, strText)) {
|
||||||
|
stack.Push(v.Name);
|
||||||
|
nCounter++;
|
||||||
|
} else {
|
||||||
|
v.IsOpen = bFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nCounter; i++) items.Remove(stack.Pop(), false);
|
||||||
|
return bFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AddSTNode(Type stNodeType, STNodeTreeCollection items, string strLibName, bool bShowException) {
|
||||||
|
if (m_dic_all_type.ContainsKey(stNodeType)) return false;
|
||||||
|
if (stNodeType == null) return false;
|
||||||
|
if (!stNodeType.IsSubclassOf(m_type_node_base)) {
|
||||||
|
if (bShowException)
|
||||||
|
throw new ArgumentException("Unsupported type [" + stNodeType.FullName + "] [stNodeType] parameter value must be the type of [STNode] subclass");
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
var attr = this.GetNodeAttribute(stNodeType);
|
||||||
|
if (attr == null) {
|
||||||
|
if (bShowException)
|
||||||
|
throw new InvalidOperationException("Type [" + stNodeType.FullName + "] is not marked by [STNodeAttribute]");
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
string strPath = string.Empty;
|
||||||
|
items.STNodeCount++;
|
||||||
|
if (!string.IsNullOrEmpty(attr.Path)) {
|
||||||
|
var strKeys = attr.Path.Split (m_chr_splitter);
|
||||||
|
for (int i = 0; i < strKeys.Length; i++) {
|
||||||
|
items = items.Add(strKeys[i]);
|
||||||
|
items.STNodeCount++;
|
||||||
|
strPath += "/" + strKeys[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
STNode node = (STNode)Activator.CreateInstance(stNodeType);
|
||||||
|
STNodeTreeCollection stt = new STNodeTreeCollection(node.Title);
|
||||||
|
stt.Path = (strLibName + "/" + attr.Path).Trim('/');
|
||||||
|
stt.STNodeType = stNodeType;
|
||||||
|
items[stt.Name] = stt;
|
||||||
|
stt.STNodeTypeColor = node.TitleColor;
|
||||||
|
m_dic_all_type.Add(stNodeType, stt.Path);
|
||||||
|
this.Invalidate();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (bShowException) throw ex;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private STNodeTreeCollection AddAssemblyPrivate(string strFile) {
|
||||||
|
strFile = System.IO.Path.GetFullPath(strFile);
|
||||||
|
var asm = Assembly.LoadFrom(strFile);
|
||||||
|
STNodeTreeCollection items = new STNodeTreeCollection(System.IO.Path.GetFileNameWithoutExtension(strFile));
|
||||||
|
foreach (var v in asm.GetTypes()) {
|
||||||
|
if (v.IsAbstract) continue;
|
||||||
|
if (v.IsSubclassOf(m_type_node_base)) this.AddSTNode(v, items, items.Name, false);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private STNodeAttribute GetNodeAttribute(Type stNodeType) {
|
||||||
|
if (stNodeType == null) return null;
|
||||||
|
foreach (var v in stNodeType.GetCustomAttributes(true)) {
|
||||||
|
if (!(v is STNodeAttribute)) continue;
|
||||||
|
return (STNodeAttribute)v;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private STNodeTreeCollection FindItemByPoint(STNodeTreeCollection items, Point pt) {
|
||||||
|
foreach (STNodeTreeCollection t in items) {
|
||||||
|
if (t.DisplayRectangle.Contains(pt)) return t;
|
||||||
|
if (t.IsOpen) {
|
||||||
|
var n = FindItemByPoint (t, pt);
|
||||||
|
if (n != null) return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region overide method ==========
|
||||||
|
|
||||||
|
protected override void OnCreateControl() {
|
||||||
|
base.OnCreateControl();
|
||||||
|
m_tbx.Top = (m_nItemHeight - m_tbx.Height) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnResize(EventArgs e) {
|
||||||
|
base.OnResize(e);
|
||||||
|
m_tbx.Width = this.Width - 29;
|
||||||
|
m_rect_clear = new Rectangle(this.Width - 20, 9, 12, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e) {
|
||||||
|
base.OnPaint(e);
|
||||||
|
m_nOffsetY = string.IsNullOrEmpty(m_str_search) ? m_nSourceOffsetY : m_nSearchOffsetY;
|
||||||
|
Graphics g = e.Graphics;
|
||||||
|
m_dt.Graphics = g;
|
||||||
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
|
||||||
|
g.TranslateTransform(0, m_nOffsetY);
|
||||||
|
int nCounter = 0;
|
||||||
|
foreach (STNodeTreeCollection v in m_items_draw)
|
||||||
|
nCounter = this.OnStartDrawItem(m_dt, v, nCounter, 0);
|
||||||
|
m_nVHeight = (nCounter + 1) * m_nItemHeight;
|
||||||
|
foreach (STNodeTreeCollection v in m_items_draw)
|
||||||
|
this.OnDrawSwitch(m_dt, v);
|
||||||
|
g.ResetTransform();
|
||||||
|
this.OnDrawSearch(m_dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseMove(MouseEventArgs e) {
|
||||||
|
base.OnMouseMove(e);
|
||||||
|
bool bRedraw = false;
|
||||||
|
m_pt_offsety = m_pt_control = e.Location;
|
||||||
|
m_pt_offsety.Y -= m_nOffsetY;
|
||||||
|
if (!string.IsNullOrEmpty(m_str_search) && m_rect_clear.Contains(e.Location))
|
||||||
|
this.Cursor = Cursors.Hand;
|
||||||
|
else
|
||||||
|
this.Cursor = Cursors.Arrow;
|
||||||
|
var node = this.FindItemByPoint(m_items_draw, m_pt_offsety);
|
||||||
|
if (m_item_hover != node) {
|
||||||
|
m_item_hover = node;
|
||||||
|
bRedraw = true;
|
||||||
|
}
|
||||||
|
if (node != null) {
|
||||||
|
bool bHoverInfo = node.InfoRectangle.Contains(m_pt_offsety);
|
||||||
|
if (bHoverInfo != m_bHoverInfo) {
|
||||||
|
m_bHoverInfo = bHoverInfo;
|
||||||
|
bRedraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bRedraw) this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseDown(MouseEventArgs e) {
|
||||||
|
base.OnMouseDown(e);
|
||||||
|
this.Focus();
|
||||||
|
if (!string.IsNullOrEmpty(m_str_search) && m_rect_clear.Contains(e.Location)) {
|
||||||
|
m_tbx.Text = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_pt_offsety = m_pt_control = e.Location;
|
||||||
|
m_pt_offsety.Y -= m_nOffsetY;
|
||||||
|
if (m_item_hover == null) return;
|
||||||
|
if (m_item_hover.SwitchRectangle.Contains(m_pt_offsety)) {
|
||||||
|
m_item_hover.IsOpen = !m_item_hover.IsOpen;
|
||||||
|
this.Invalidate();
|
||||||
|
} else if (m_item_hover.InfoRectangle.Contains(m_pt_offsety)) {
|
||||||
|
Rectangle rect = this.RectangleToScreen(m_item_hover.DisplayRectangle);
|
||||||
|
FrmNodePreviewPanel frm = new FrmNodePreviewPanel(m_item_hover.STNodeType,
|
||||||
|
new Point(rect.Right - m_nItemHeight, rect.Top + m_nOffsetY),
|
||||||
|
m_nItemHeight,
|
||||||
|
this._InfoPanelIsLeftLayout,
|
||||||
|
this._Editor, this._PropertyGrid);
|
||||||
|
frm.BackColor = this.BackColor;
|
||||||
|
frm.Show(this);
|
||||||
|
} else if (m_item_hover.STNodeType != null) {
|
||||||
|
DataObject d = new DataObject("STNodeType", m_item_hover.STNodeType);
|
||||||
|
this.DoDragDrop(d, DragDropEffects.Copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseDoubleClick(MouseEventArgs e) {
|
||||||
|
base.OnMouseDoubleClick(e);
|
||||||
|
m_pt_offsety = m_pt_control = e.Location;
|
||||||
|
m_pt_offsety.Y -= m_nOffsetY;
|
||||||
|
STNodeTreeCollection item = this.FindItemByPoint(m_items_draw, m_pt_offsety);
|
||||||
|
if (item == null || item.STNodeType != null) return;
|
||||||
|
item.IsOpen = !item.IsOpen;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseLeave(EventArgs e) {
|
||||||
|
base.OnMouseLeave(e);
|
||||||
|
if (m_item_hover != null) {
|
||||||
|
m_item_hover = null;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseWheel(MouseEventArgs e) {
|
||||||
|
base.OnMouseWheel(e);
|
||||||
|
if (e.Delta > 0) {
|
||||||
|
if (m_nOffsetY == 0) return;
|
||||||
|
m_nOffsetY += m_nItemHeight;
|
||||||
|
if (m_nOffsetY > 0) m_nOffsetY = 0;
|
||||||
|
} else {
|
||||||
|
if (this.Height - m_nOffsetY >= m_nVHeight) return;
|
||||||
|
m_nOffsetY -= m_nItemHeight;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(m_str_search))
|
||||||
|
m_nSourceOffsetY = m_nOffsetY;
|
||||||
|
else
|
||||||
|
m_nSearchOffsetY = m_nOffsetY;
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region protected method ==========
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the retrieved text area is drawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
protected virtual void OnDrawSearch(DrawingTools dt) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
m_brush.Color = this._TitleColor;
|
||||||
|
g.FillRectangle(m_brush, 0, 0, this.Width, m_nItemHeight);
|
||||||
|
m_brush.Color = m_tbx.BackColor;
|
||||||
|
g.FillRectangle(m_brush, 5, 5, this.Width - 10, m_nItemHeight - 10);
|
||||||
|
m_pen.Color = this.ForeColor;
|
||||||
|
if (string.IsNullOrEmpty(m_str_search)) {
|
||||||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
||||||
|
g.DrawEllipse(m_pen, this.Width - 17, 8, 8, 8);
|
||||||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
||||||
|
g.DrawLine(m_pen, this.Width - 13, 17, this.Width - 13, m_nItemHeight - 9);
|
||||||
|
} else {
|
||||||
|
m_pen.Color = this.ForeColor;
|
||||||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
||||||
|
g.DrawEllipse(m_pen, this.Width - 20, 9, 10, 10);
|
||||||
|
g.DrawLine(m_pen, this.Width - 18, 11, this.Width - 12, 17);
|
||||||
|
g.DrawLine(m_pen, this.Width - 12, 11, this.Width - 18, 17);
|
||||||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when starting to draw each node of the tree node
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
/// <param name="Items">The current collection that needs to be drawn</param>
|
||||||
|
/// <param name="nCounter">Counter of drawn counts</param>
|
||||||
|
/// <param name="nLevel">Currently located in the sub-collection of which level</param>
|
||||||
|
/// <returns>Number of already drawn</returns>
|
||||||
|
protected virtual int OnStartDrawItem(DrawingTools dt, STNodeTreeCollection Items, int nCounter, int nLevel) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
Items.DisplayRectangle = new Rectangle(0, m_nItemHeight * (nCounter + 1), this.Width, m_nItemHeight);
|
||||||
|
Items.SwitchRectangle = new Rectangle(5 + nLevel * 10, (nCounter + 1) * m_nItemHeight, 10, m_nItemHeight);
|
||||||
|
if (this._ShowInfoButton && Items.STNodeType != null)
|
||||||
|
Items.InfoRectangle = new Rectangle(this.Width - 18, Items.DisplayRectangle.Top + (m_nItemHeight - 14) / 2, 14, 14);
|
||||||
|
else Items.InfoRectangle = Rectangle.Empty;
|
||||||
|
this.OnDrawItem(dt, Items, nCounter++, nLevel);
|
||||||
|
if (!Items.IsOpen) return nCounter;
|
||||||
|
foreach (STNodeTreeCollection n in Items) {
|
||||||
|
if (n.STNodeType == null)
|
||||||
|
nCounter = this.OnStartDrawItem(dt, n, nCounter++, nLevel + 1);
|
||||||
|
}
|
||||||
|
foreach (STNodeTreeCollection n in Items) {
|
||||||
|
if (n.STNodeType != null)
|
||||||
|
nCounter = this.OnStartDrawItem(dt, n, nCounter++, nLevel + 1);
|
||||||
|
}
|
||||||
|
foreach (STNodeTreeCollection v in Items) this.OnDrawSwitch(dt, v);
|
||||||
|
return nCounter;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when each node of the tree node is drawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
/// <param name="items">The current collection that needs to be drawn</param>
|
||||||
|
/// <param name="nCounter">Counter of drawn counts</param>
|
||||||
|
/// <param name="nLevel">Currently located in the sub-collection of which level</param>
|
||||||
|
protected virtual void OnDrawItem(DrawingTools dt, STNodeTreeCollection items, int nCounter, int nLevel) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
m_brush.Color = nCounter % 2 == 0 ? m_clr_item_1 : m_clr_item_2;
|
||||||
|
g.FillRectangle(m_brush, items.DisplayRectangle);
|
||||||
|
if (items == m_item_hover) {
|
||||||
|
m_brush.Color = this._ItemHoverColor;
|
||||||
|
g.FillRectangle(m_brush, items.DisplayRectangle);
|
||||||
|
}
|
||||||
|
Rectangle rect = new Rectangle(45 + nLevel * 10, items.SwitchRectangle.Top, this.Width - 45 - nLevel * 10, m_nItemHeight);
|
||||||
|
m_pen.Color = Color.FromArgb(100, 125, 125, 125);
|
||||||
|
g.DrawLine(m_pen, 9, items.SwitchRectangle.Top + m_nItemHeight / 2, items.SwitchRectangle.Left + 19, items.SwitchRectangle.Top + m_nItemHeight / 2);
|
||||||
|
if (nCounter != 0) {
|
||||||
|
for (int i = 0; i <= nLevel; i++) {
|
||||||
|
g.DrawLine(m_pen, 9 + i * 10, items.SwitchRectangle.Top - m_nItemHeight / 2, 9 + i * 10, items.SwitchRectangle.Top + m_nItemHeight / 2 - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.OnDrawItemText(dt, items, rect);
|
||||||
|
this.OnDrawItemIcon(dt, items, rect);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when drawing the tree node expand and close switches
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
/// <param name="items">The current collection that needs to be drawn</param>
|
||||||
|
protected virtual void OnDrawSwitch(DrawingTools dt, STNodeTreeCollection items) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
if (items.Count != 0) {
|
||||||
|
m_pen.Color = this._SwitchColor;
|
||||||
|
m_brush.Color = m_pen.Color;
|
||||||
|
int nT = items.SwitchRectangle.Y + m_nItemHeight / 2 - 4;
|
||||||
|
g.DrawRectangle(m_pen, items.SwitchRectangle.Left, nT, 8, 8);
|
||||||
|
g.DrawLine(m_pen, items.SwitchRectangle.Left + 1, nT + 4, items.SwitchRectangle.Right - 3, nT + 4);
|
||||||
|
if (items.IsOpen) return;
|
||||||
|
g.DrawLine(m_pen, items.SwitchRectangle.Left + 4, nT + 1, items.SwitchRectangle.Left + 4, nT + 7);
|
||||||
|
//if (items.IsOpen) {
|
||||||
|
// //g.FillPolygon(m_brush, new Point[]{
|
||||||
|
// // new Point(items.DotRectangle.Left + 0, items.DotRectangle.Top + m_nItemHeight / 2 - 2),
|
||||||
|
// // new Point(items.DotRectangle.Left + 9, items.DotRectangle.Top + m_nItemHeight / 2 - 2),
|
||||||
|
// // new Point(items.DotRectangle.Left + 4, items.DotRectangle.Top + m_nItemHeight / 2 + 3)
|
||||||
|
// //});
|
||||||
|
// g.DrawRectangle(m_pen, items.SwitchRectangle.Left, nT, 8, 8);
|
||||||
|
// g.DrawLine(m_pen, items.SwitchRectangle.Left + 1, nT + 4, items.SwitchRectangle.Right - 3, nT + 4);
|
||||||
|
//} else {
|
||||||
|
// //g.FillPolygon(m_brush, new Point[]{
|
||||||
|
// // new Point(items.DotRectangle.Left + 2, items.DotRectangle.Top + m_nItemHeight / 2 - 5),
|
||||||
|
// // new Point(items.DotRectangle.Left + 2, items.DotRectangle.Top + m_nItemHeight / 2 + 5),
|
||||||
|
// // new Point(items.DotRectangle.Left + 7, items.DotRectangle.Top + m_nItemHeight / 2)
|
||||||
|
// //});
|
||||||
|
// g.DrawRectangle(m_pen, items.SwitchRectangle.Left, nT, 8, 8);
|
||||||
|
// g.DrawLine(m_pen, items.SwitchRectangle.Left + 1, nT + 4, items.SwitchRectangle.Right - 3, nT + 4);
|
||||||
|
// g.DrawLine(m_pen, items.SwitchRectangle.Left + 4, nT + 1, items.SwitchRectangle.Left + 4, nT + 7);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the text of the tree node is drawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
/// <param name="items">The current collection that needs to be drawn</param>
|
||||||
|
/// <param name="rect">The rectangular area where the text field is located</param>
|
||||||
|
protected virtual void OnDrawItemText(DrawingTools dt, STNodeTreeCollection items, Rectangle rect) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
rect.Width -= 20;
|
||||||
|
m_sf.FormatFlags = StringFormatFlags.NoWrap;
|
||||||
|
if (!string.IsNullOrEmpty(m_str_search)) {
|
||||||
|
int nIndex = items.NameLower.IndexOf(m_str_search);
|
||||||
|
if (nIndex != -1) {
|
||||||
|
CharacterRange[] chrs = { new CharacterRange(nIndex, m_str_search.Length) };//global
|
||||||
|
m_sf.SetMeasurableCharacterRanges(chrs);
|
||||||
|
Region[] regions = g.MeasureCharacterRanges(items.Name, this.Font, rect, m_sf);
|
||||||
|
g.SetClip(regions[0], System.Drawing.Drawing2D.CombineMode.Intersect);
|
||||||
|
m_brush.Color = this._HightLightTextColor;
|
||||||
|
g.DrawString(items.Name, this.Font, m_brush, rect, m_sf);
|
||||||
|
g.ResetClip();
|
||||||
|
g.SetClip(regions[0], System.Drawing.Drawing2D.CombineMode.Exclude);
|
||||||
|
m_brush.Color = items.STNodeType == null ? Color.FromArgb(this.ForeColor.A * 1 / 2, this.ForeColor) : this.ForeColor;
|
||||||
|
g.DrawString(items.Name, this.Font, m_brush, rect, m_sf);
|
||||||
|
g.ResetClip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_brush.Color = items.STNodeType == null ? Color.FromArgb(this.ForeColor.A * 2 / 3, this.ForeColor) : this.ForeColor;
|
||||||
|
g.DrawString(items.Name, this.Font, m_brush, rect, m_sf);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the tree node icon is drawn
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dt">Drawing tool</param>
|
||||||
|
/// <param name="items">The current collection that needs to be drawn</param>
|
||||||
|
/// <param name="rect">The rectangular area where the text field is located</param>
|
||||||
|
protected virtual void OnDrawItemIcon(DrawingTools dt, STNodeTreeCollection items, Rectangle rect) {
|
||||||
|
Graphics g = dt.Graphics;
|
||||||
|
if (items.STNodeType != null) {
|
||||||
|
m_pen.Color = this._AutoColor ? items.STNodeTypeColor : Color.DarkCyan;
|
||||||
|
m_brush.Color = Color.LightGray;
|
||||||
|
g.DrawRectangle(m_pen, rect.Left - 15, rect.Top + m_nItemHeight / 2 - 5, 11, 10);
|
||||||
|
g.FillRectangle(m_brush, rect.Left - 17, rect.Top + m_nItemHeight / 2 - 2, 5, 5);
|
||||||
|
g.FillRectangle(m_brush, rect.Left - 6, rect.Top + m_nItemHeight / 2 - 2, 5, 5);
|
||||||
|
if (m_item_hover == items && m_bHoverInfo) {
|
||||||
|
m_brush.Color = this.BackColor;
|
||||||
|
g.FillRectangle(m_brush, items.InfoRectangle);
|
||||||
|
}
|
||||||
|
m_pen.Color = this._AutoColor ? items.STNodeTypeColor : this._InfoButtonColor;
|
||||||
|
m_pen.Width = 2;
|
||||||
|
g.DrawLine(m_pen, items.InfoRectangle.X + 4, items.InfoRectangle.Y + 3, items.InfoRectangle.X + 10, items.InfoRectangle.Y + 3);
|
||||||
|
g.DrawLine(m_pen, items.InfoRectangle.X + 4, items.InfoRectangle.Y + 6, items.InfoRectangle.X + 10, items.InfoRectangle.Y + 6);
|
||||||
|
g.DrawLine(m_pen, items.InfoRectangle.X + 4, items.InfoRectangle.Y + 11, items.InfoRectangle.X + 10, items.InfoRectangle.Y + 11);
|
||||||
|
g.DrawLine(m_pen, items.InfoRectangle.X + 7, items.InfoRectangle.Y + 7, items.InfoRectangle.X + 7, items.InfoRectangle.Y + 10);
|
||||||
|
m_pen.Width = 1;
|
||||||
|
g.DrawRectangle(m_pen, items.InfoRectangle.X, items.InfoRectangle.Y, items.InfoRectangle.Width - 1, items.InfoRectangle.Height - 1);
|
||||||
|
} else {
|
||||||
|
if (items.IsLibraryRoot) {
|
||||||
|
Rectangle rect_box = new Rectangle(rect.Left - 15, rect.Top + m_nItemHeight / 2 - 5, 11, 10);
|
||||||
|
g.DrawRectangle(Pens.Gray, rect_box);
|
||||||
|
g.DrawLine(Pens.Cyan, rect_box.X - 2, rect_box.Top, rect_box.X + 2, rect_box.Top);
|
||||||
|
g.DrawLine(Pens.Cyan, rect_box.X, rect_box.Y - 2, rect_box.X, rect_box.Y + 2);
|
||||||
|
g.DrawLine(Pens.Cyan, rect_box.Right - 2, rect_box.Bottom, rect_box.Right + 2, rect_box.Bottom);
|
||||||
|
g.DrawLine(Pens.Cyan, rect_box.Right, rect_box.Bottom - 2, rect_box.Right, rect_box.Bottom + 2);
|
||||||
|
} else {
|
||||||
|
g.DrawRectangle(Pens.Goldenrod, new Rectangle(rect.Left - 16, rect.Top + m_nItemHeight / 2 - 6, 8, 3));
|
||||||
|
g.DrawRectangle(Pens.Goldenrod, new Rectangle(rect.Left - 16, rect.Top + m_nItemHeight / 2 - 3, 13, 9));
|
||||||
|
}
|
||||||
|
if (!this._ShowFolderCount) return;
|
||||||
|
m_sf.Alignment = StringAlignment.Far;
|
||||||
|
m_brush.Color = this._FolderCountColor;
|
||||||
|
rect.X -= 4;
|
||||||
|
g.DrawString("[" + items.STNodeCount.ToString() + "]", this.Font, m_brush, rect, m_sf);
|
||||||
|
m_sf.Alignment = StringAlignment.Near;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region public method ==========
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve the STNode in the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strText">Text to retrieve</param>
|
||||||
|
public void Search(string strText) {
|
||||||
|
if (strText == null) return;
|
||||||
|
if (strText.Trim() == string.Empty) return;
|
||||||
|
m_tbx.Text = strText.Trim ();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Add a STNode type to the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stNodeType">STNode type</param>
|
||||||
|
/// <returns>Whether the addition is successful</returns>
|
||||||
|
public bool AddNode(Type stNodeType) { return this.AddSTNode(stNodeType, m_items_source, null, true); }
|
||||||
|
/// <summary>
|
||||||
|
/// Add the STNode type to the control from the file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strFile">Specify file path</param>
|
||||||
|
/// <returns>Add success number</returns>
|
||||||
|
public int LoadAssembly(string strFile) {
|
||||||
|
strFile = System.IO.Path.GetFullPath(strFile);
|
||||||
|
var items = this.AddAssemblyPrivate(strFile);
|
||||||
|
if (items.STNodeCount == 0) return 0;
|
||||||
|
items.IsLibraryRoot = true;
|
||||||
|
m_items_source[items.Name] = items;
|
||||||
|
return items.STNodeCount;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all STNode types in the control
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() {
|
||||||
|
m_items_source.Clear();
|
||||||
|
m_items_draw.Clear();
|
||||||
|
m_dic_all_type.Clear();
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Remove an STNode type from the control
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stNodeType">STNode type</param>
|
||||||
|
/// <returns>Whether the removal is successful</returns>
|
||||||
|
public bool RemoveNode(Type stNodeType) {
|
||||||
|
if (!m_dic_all_type.ContainsKey(stNodeType)) return false;
|
||||||
|
string strPath = m_dic_all_type[stNodeType];
|
||||||
|
STNodeTreeCollection items = m_items_source;
|
||||||
|
if (!string.IsNullOrEmpty(strPath)) {
|
||||||
|
string[] strKeys = strPath.Split(m_chr_splitter);
|
||||||
|
for (int i = 0; i < strKeys.Length; i++) {
|
||||||
|
items = items[strKeys[i]];
|
||||||
|
if (items == null) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
STNode node = (STNode)Activator.CreateInstance(stNodeType);
|
||||||
|
if (items[node.Title] == null) return false;
|
||||||
|
items.Remove(node.Title, true);
|
||||||
|
m_dic_all_type.Remove(stNodeType);
|
||||||
|
} catch { return false; }
|
||||||
|
this.Invalidate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
//=================================================================================================
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of each item in the STNodeTreeView control
|
||||||
|
/// </summary>
|
||||||
|
protected class STNodeTreeCollection : IEnumerable
|
||||||
|
{
|
||||||
|
private string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current tree node display name
|
||||||
|
/// </summary>
|
||||||
|
public string Name {
|
||||||
|
get {
|
||||||
|
return _Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the lowercase string of the display name of the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public string NameLower { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the STNode type corresponding to the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public Type STNodeType { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the parent tree node of the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public STNodeTreeCollection Parent { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the number of STNode types owned by the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public int STNodeCount { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the corresponding path of the STNode type corresponding to the current tree node in the tree control
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current or set whether the tree node is open
|
||||||
|
/// </summary>
|
||||||
|
public bool IsOpen { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get whether the current tree node is the root node of the loaded module
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLibraryRoot { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the display area of the current tree node in the control
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle DisplayRectangle { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the switch button area of the current tree node in the control
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle SwitchRectangle { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the information button area of the current tree node in the control
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle InfoRectangle { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the title color of the STNode type corresponding to the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public Color STNodeTypeColor { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Get the number of child nodes contained in the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get { return m_dic.Count; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the collection with the specified name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strKey">specify name</param>
|
||||||
|
/// <returns>Collection</returns>
|
||||||
|
public STNodeTreeCollection this[string strKey] {
|
||||||
|
get {
|
||||||
|
if (string.IsNullOrEmpty(strKey)) return null;
|
||||||
|
if (m_dic.ContainsKey(strKey)) return m_dic[strKey];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(strKey)) return;
|
||||||
|
if (value == null) return;
|
||||||
|
if (m_dic.ContainsKey(strKey)) {
|
||||||
|
m_dic[strKey] = value;
|
||||||
|
} else {
|
||||||
|
m_dic.Add(strKey, value);
|
||||||
|
}
|
||||||
|
value.Parent = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortedDictionary<string, STNodeTreeCollection> m_dic = new SortedDictionary<string, STNodeTreeCollection>();
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a tree node collection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strName">Display name of the current tree node in the control</param>
|
||||||
|
public STNodeTreeCollection(string strName) {
|
||||||
|
if (strName == null || strName.Trim() == string.Empty)
|
||||||
|
throw new ArgumentNullException("Display name cannot be null");
|
||||||
|
this._Name = strName.Trim();
|
||||||
|
this.NameLower = this._Name.ToLower();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Add a child node to the current tree node
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strName">node display name</param>
|
||||||
|
/// <returns>Added set of child nodes</returns>
|
||||||
|
public STNodeTreeCollection Add(string strName) {
|
||||||
|
if (!m_dic.ContainsKey(strName))
|
||||||
|
m_dic.Add(strName, new STNodeTreeCollection(strName) { Parent = this });
|
||||||
|
return m_dic[strName];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Delete a subcollection from the current tree node
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strName">Subcollection name</param>
|
||||||
|
/// <param name="isAutoDelFolder">Whether to automatically clear useless nodes recursively upwards</param>
|
||||||
|
/// <returns>Whether the deletion is successful</returns>
|
||||||
|
public bool Remove(string strName, bool isAutoDelFolder) {
|
||||||
|
if (!m_dic.ContainsKey(strName)) return false;
|
||||||
|
bool b = m_dic.Remove(strName);
|
||||||
|
var temp = this;
|
||||||
|
while (temp != null) {
|
||||||
|
temp.STNodeCount--;
|
||||||
|
temp = temp.Parent;
|
||||||
|
}
|
||||||
|
if (isAutoDelFolder && m_dic.Count == 0 && this.Parent != null)
|
||||||
|
return b && this.Parent.Remove(this.Name, isAutoDelFolder);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all child nodes in the current tree node
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() { this.Clear(this); }
|
||||||
|
|
||||||
|
private void Clear(STNodeTreeCollection items) {
|
||||||
|
foreach (STNodeTreeCollection v in items) v.Clear(v);
|
||||||
|
m_dic.Clear();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get all the name arrays in the current tree node
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string[] GetKeys() { return m_dic.Keys.ToArray(); }
|
||||||
|
/// <summary>
|
||||||
|
/// Copy all data in the current tree node set
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Copy copy</returns>
|
||||||
|
public STNodeTreeCollection Copy() {
|
||||||
|
STNodeTreeCollection items = new STNodeTreeCollection("COPY");
|
||||||
|
this.Copy(this, items);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Copy(STNodeTreeCollection items_src, STNodeTreeCollection items_dst) {
|
||||||
|
foreach (STNodeTreeCollection v in items_src) {
|
||||||
|
this.Copy(v, items_dst.Add(v.Name));
|
||||||
|
}
|
||||||
|
items_dst.Path = items_src.Path;
|
||||||
|
items_dst.STNodeType = items_src.STNodeType;
|
||||||
|
items_dst.IsLibraryRoot = items_src.IsLibraryRoot;
|
||||||
|
items_dst.STNodeCount = items_src.STNodeCount;
|
||||||
|
items_dst.STNodeTypeColor = items_src.STNodeTypeColor;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an Array of System.Collections.IEnumerator
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IEnumerator GetEnumerator() {
|
||||||
|
foreach (var v in m_dic.Values) yield return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return this.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user