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; /// /// 获取当前 Option 所属的 Node /// public STNode Owner { get { return _Owner; } internal set { if (value == _Owner) return; if (_Owner != null) this.DisConnectionAll(); //当所有者变更时 断开当前所有连接 _Owner = value; } } private bool _IsSingle; /// /// 获取当前 Option 是否仅能被连接一次 /// public bool IsSingle { get { return _IsSingle; } } private bool _IsInput; /// /// 获取当前 Option 是否是输入选项 /// public bool IsInput { get { return _IsInput; } internal set { _IsInput = value; } } private Color _TextColor = Color.White; /// /// 获取或设置当前 Option 文本颜色 /// public Color TextColor { get { return _TextColor; } internal set { if (value == _TextColor) return; _TextColor = value; this.Invalidate(); } } private Color _DotColor = Color.Transparent; /// /// 获取或设置当前 Option 连接点的颜色 /// public Color DotColor { get { return _DotColor; } internal set { if (value == _DotColor) return; _DotColor = value; this.Invalidate(); } } private string _Text; /// /// 获取或设置当前 Option 显示文本 /// 当AutoSize被设置时 无法修改此属性 /// 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; /// /// 获取当前 Option 连接点的左边坐标 /// public int DotLeft { get { return _DotLeft; } internal set { _DotLeft = value; } } private int _DotTop; /// /// 获取当前 Option 连接点的上边坐标 /// public int DotTop { get { return _DotTop; } internal set { _DotTop = value; } } private int _DotSize; /// /// 获取当前 Option 连接点的宽度 /// public int DotSize { get { return _DotSize; } protected set { _DotSize = value; } } private Rectangle _TextRectangle; /// /// 获取当前 Option 文本区域 /// public Rectangle TextRectangle { get { return _TextRectangle; } internal set { _TextRectangle = value; } } private object _Data; /// /// 获取或者设置当前 Option 所包含的数据 /// 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("无效数据类型 数据类型必须为指定的数据类型或其子类"); } } _Data = value; } } private Type _DataType; /// /// 获取当前 Option 数据类型 /// public Type DataType { get { return _DataType; } internal set { _DataType = value; } } //private Rectangle _DotRectangle; /// /// 获取当前 Option 连接点的区域 /// public Rectangle DotRectangle { get { return new Rectangle(this._DotLeft, this._DotTop, this._DotSize, this._DotSize); } } /// /// 获取当前 Option 被连接的个数 /// public int ConnectionCount { get { return m_hs_connected.Count; } } /// /// 获取当前 Option 所连接的 Option 集合 /// internal HashSet ConnectedOption { get { return m_hs_connected; } } #endregion Properties /// /// 保存已经被连接的点 /// protected HashSet m_hs_connected; #region Constructor private STNodeOption() { } /// /// 构造一个 Option /// /// 显示文本 /// 数据类型 /// 是否为单连接 public STNodeOption(string strText, Type dataType, bool bSingle) { if (dataType == null) throw new ArgumentNullException("指定的数据类型不能为空"); this._DotSize = 10; m_hs_connected = new HashSet(); this._DataType = dataType; this._Text = strText; this._IsSingle = bSingle; } #endregion Constructor #region Event /// /// 当被连接时候发生 /// public event STNodeOptionEventHandler Connected; /// /// 当连接开始发生时发生 /// public event STNodeOptionEventHandler Connecting; /// /// 当连接断开时候发生 /// public event STNodeOptionEventHandler DisConnected; /// /// 当连接开始断开时发生 /// public event STNodeOptionEventHandler DisConnecting; /// /// 当有数据传递时候发生 /// public event STNodeOptionEventHandler DataTransfer; #endregion Event #region protected /// /// 重绘整个控件 /// protected void Invalidate() { if (this._Owner == null) return; this._Owner.Invalidate(); } /* * 开始我认为应当只有输入类型的选项才具有事件 因为输入是被动的 而输出则是主动的 * 但是后来发现 比如在STNodeHub中输出节点就用到了事件 * 以防万一所以这里代码注释起来了 也并不是很有问题 输出选项不注册事件也是一样的效果 */ 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); } /// /// 当前 Option 开始连接目标 Option /// /// 需要连接的 Option /// 是否允许继续操作 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; } /// /// 当前 Option 开始断开目标 Option /// /// 需要断开的 Option /// 是否允许继续操作 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 /// /// 当前 Option 连接目标 Option /// /// 需要连接的 Option /// 连接结果 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; } /// /// 检测当前 Option 是否可以连接目标 Option /// /// 需要连接的 Option /// 检测结果 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; } /// /// 当前 Option 断开目标 Option /// /// 需要断开的 Option /// 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; } /// /// 断开当前 Option 的所有连接 /// public void DisConnectionAll() { if (this._DataType == null) return; var arr = m_hs_connected.ToArray(); foreach (var v in arr) { this.DisConnectOption(v); } } /// /// 获取当前 Option 所连接的 Option 集合 /// /// 如果为null 则表示不存在所有者 否则返回集合 public List GetConnectedOption() { if (this._DataType == null) return null; if (!this._IsInput) return m_hs_connected.ToList(); List lst = new List(); 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; } /// /// 向当前 Option 所连接的所有 Option 投递数据 /// public void TransferData() { if (this._DataType == null) return; foreach (var v in m_hs_connected) { v.OnDataTransfer(new STNodeOptionEventArgs(true, this, ConnectionStatus.Connected)); } } /// /// 向当前 Option 所连接的所有 Option 投递数据 /// /// 需要投递的数据 public void TransferData(object data) { if (this._DataType == null) return; this.Data = data; //不是this._Data foreach (var v in m_hs_connected) { v.OnDataTransfer(new STNodeOptionEventArgs(true, this, ConnectionStatus.Connected)); } } /// /// 向当前 Option 所连接的所有 Option 投递数据 /// /// 需要投递的数据 /// 是否释放旧数据 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 } }